//
//  Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOSTER_LOCALE_TEST_H
#define BOOSTER_LOCALE_TEST_H
#include <booster/backtrace.h>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <string>

int error_counter=0;
int test_counter=0;

#ifndef BOOSTER_LOCALE_ERROR_LIMIT
#define BOOSTER_LOCALE_ERROR_LIMIT 20
#endif


#define THROW_IF_TOO_BIG(X)                                                     \
do {                                                                            \
    if((X) > BOOSTER_LOCALE_ERROR_LIMIT)                                          \
        throw booster::runtime_error("Error limits reached, stopping unit test");   \
}while(0)

#define TEST(X)                                                         \
    do {                                                                \
        test_counter++;                                                 \
        if(X) break;                                                    \
        std::cerr << "Error in line:"<<__LINE__ << " "#X  << std::endl; \
        THROW_IF_TOO_BIG(error_counter++);                              \
    }while(0)    
#endif

#define TEST_THROWS(X,E)                                                \
    do {                                                                \
        test_counter++;                                                 \
        try { X; } catch(E const &/*e*/ ) {break;} catch(...){}         \
        std::cerr << "Error in line:"<<__LINE__ << " "#X  << std::endl; \
        THROW_IF_TOO_BIG(error_counter++);                              \
    }while(0)    

#define FINALIZE()                                                      \
    do {                                                                \
        int passed=test_counter - error_counter;                        \
        std::cout << std::endl;                                         \
        std::cout << "Passed "<<passed<<" tests" << std::endl;          \
        if(error_counter >0 ) {                                         \
            std::cout << "Failed "<<error_counter<<" tests"<<std::endl; \
        }                                                               \
        std::cout <<" "<< std::fixed << std::setprecision(1)            \
                << std::setw(5) << 100.0 * passed / test_counter <<     \
                "% of tests completed sucsessefully" << std::endl;      \
        return error_counter == 0 ? EXIT_SUCCESS : EXIT_FAILURE ;       \
    }while(0)


inline unsigned utf8_next(std::string const &s,unsigned &pos)
{
    unsigned c=(unsigned char)s[pos++];
    if( (unsigned char)(c - 0xc0) >= 0x35)
        return c;
    unsigned l;
    if(c < 192)
        l = 0;
    else if(c < 224)
        l = 1;
    else if(c < 240)
        l = 2;
    else
        l = 3;
    
    c&=(1<<(6-l))-1;
    
    switch(l) {
    case 3:
        c = (c << 6) | (((unsigned char)s[pos++]) & 0x3F);
    case 2:
        c = (c << 6) | (((unsigned char)s[pos++]) & 0x3F);
    case 1:
        c = (c << 6) | (((unsigned char)s[pos++]) & 0x3F);
    }
    return c;
}

template<typename Char>
std::basic_string<Char> to(std::string const &utf8)
{
    std::basic_string<Char> out;
    unsigned i=0;
    while(i<utf8.size()) {
        unsigned point;
        unsigned prev=i;
        point = utf8_next(utf8,i);
        if(sizeof(Char)==1 && point > 255) {
            std::ostringstream ss;
            ss << "Can't convert codepoint U" << std::hex << point <<"(" <<std::string(utf8.begin()+prev,utf8.begin()+i)<<") to Latin1";
            throw booster::runtime_error(ss.str());
        }
        else if(sizeof(Char)==2 && point >0xFFFF) { // Deal with surragates
            point-=0x10000;
            out+=static_cast<Char>(0xD800 | (point>>10));
            out+=static_cast<Char>(0xDC00 | (point & 0x3FF));
            continue;
        }
        out+=static_cast<Char>(point);
    }
    return out;
}


// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
