// (C) Copyright Jonathan Turkanis 2004
// 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.)

// See http://www.boost.org/libs/iostreams for documentation.

#include <boost/iostreams/detail/config/wide_streams.hpp>
#ifdef BOOST_IOSTREAMS_NO_WIDE_STREAMS
# error wide streams not supported on this platform
#endif

#include <algorithm>         // equal.
#include <locale>
#include <string>
#include <boost/config.hpp>  // BOOST_DEDUCED_TYPENAME.
#include <boost/iostreams/code_converter.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/detail/add_facet.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/detail/config/windows_posix.hpp>
#include <boost/iostreams/device/file.hpp>
#if !defined(__COMO__) || !defined(BOOST_COMO_STRICT)
# if defined(BOOST_IOSTREAMS_NO_LIB) || defined(BOOST_ALL_NO_LIB)
#  include "../src/file_descriptor.cpp"
# else 
#  include <boost/iostreams/device/file_descriptor.hpp>
# endif
#endif
#include <boost/iostreams/stream.hpp>
#include <boost/test/test_tools.hpp>
#include <boost/test/unit_test.hpp>
#include "detail/temp_file.hpp"

    // Include codevct facets

#include "detail/null_padded_codecvt.hpp"
#include "detail/utf8_codecvt_facet.hpp"
#ifdef BOOST_IOSTREAMS_USE_DINKUM_COREX
# include <codecvt/8859_1> 
# include <codecvt/8859_10> 
# include <codecvt/8859_13> 
# include <codecvt/8859_14> 
# include <codecvt/8859_15> 
# include <codecvt/8859_16> 
# include <codecvt/8859_2>  
# include <codecvt/8859_3>  
# include <codecvt/8859_4>  
# include <codecvt/8859_5>  
# include <codecvt/8859_6> 
# include <codecvt/8859_7> 
# include <codecvt/8859_8> 
# include <codecvt/8859_9> 
# include <codecvt/baltic> 
# include <codecvt/big5>   
# include <codecvt/cp037>  
# include <codecvt/cp1006> 
# include <codecvt/cp1026> 
# include <codecvt/cp1250> 
# include <codecvt/cp1251> 
# include <codecvt/cp1252> 
# include <codecvt/cp1253> 
# include <codecvt/cp1254> 
# include <codecvt/cp1255> 
# include <codecvt/cp1256> 
# include <codecvt/cp1257> 
# include <codecvt/cp1258> 
# include <codecvt/cp424>  
# include <codecvt/cp437>  
# include <codecvt/cp500> 
# include <codecvt/cp737> 
# include <codecvt/cp775> 
# include <codecvt/cp850> 
# include <codecvt/cp852> 
# include <codecvt/cp855> 
# include <codecvt/cp856> 
# include <codecvt/cp857> 
# include <codecvt/cp860> 
# include <codecvt/cp861> 
# include <codecvt/cp862> 
# include <codecvt/cp863> 
# include <codecvt/cp864> 
# include <codecvt/cp865> 
# include <codecvt/cp866> 
# include <codecvt/cp869> 
# include <codecvt/cp874> 
# include <codecvt/cp875> 
# include <codecvt/cp932> 
# include <codecvt/cp936> 
# include <codecvt/cp949>    
# include <codecvt/cp950>    
# include <codecvt/cyrillic> 
# include <codecvt/ebcdic>   
# include <codecvt/euc>      
# include <codecvt/euc_0208> 
# include <codecvt/gb12345>  
# include <codecvt/gb2312>   
# include <codecvt/greek>    
# include <codecvt/iceland>  
# include <codecvt/jis>       
# include <codecvt/jis_0208>  
# include <codecvt/jis0201>   
# include <codecvt/ksc5601>   
# include <codecvt/latin2>    
# include <codecvt/one_one>   
# include <codecvt/roman>     
# include <codecvt/sjis>      
# include <codecvt/sjis_0208> 
# include <codecvt/turkish>
# include <codecvt/utf16>
# include <codecvt/utf8>
# include <codecvt/utf8_utf16>
# include <codecvt/xjis>
#endif // #ifdef BOOST_IOSTREAMS_USE_DINKUM_COREX]

#include <iostream>

using namespace std;
using namespace boost::iostreams;
using namespace boost::iostreams::detail;
using namespace boost::iostreams::test;
using boost::unit_test::test_suite;     
namespace io = boost::iostreams;

const int max_length = 30;
const unsigned int pattern_length = 100;
const unsigned int pattern_reps = 100;

template<typename Codecvt>
bool valid_char(typename codecvt_intern<Codecvt>::type c)
{
    typedef typename codecvt_state<Codecvt>::type   state_type;
    typedef typename codecvt_intern<Codecvt>::type  intern_type;
    Codecvt             cvt;
    state_type          state = state_type();
    const intern_type*  nint;
    char*               next;
    char                buf[max_length];

    return cvt.out( state, &c, &c + 1, nint,
                    buf, buf + max_length, next )
           ==
           codecvt_base::ok;
}

template<typename Codecvt>
basic_string<
    BOOST_DEDUCED_TYPENAME 
    codecvt_intern<Codecvt>::type
> 
test_string()
{
    typedef typename codecvt_intern<Codecvt>::type intern_type;
    std::basic_string<intern_type> pattern, result;
    for (intern_type c = 255; pattern.size() < pattern_length; --c)
        if (valid_char<Codecvt>(c))
            pattern += c;
    result.reserve(pattern.size() * pattern_reps);
    for (int w = 0; w < pattern_reps; ++w)
        result += pattern;
    return result;
}

// Como can't compile file_descriptor.cpp in strict mode; this failure
// is detected by file_descriptor_test.cpp.
#if !defined(__COMO__) || !defined(BOOST_COMO_STRICT)
    typedef io::file_descriptor_source  classic_file_source;
    typedef io::file_descriptor_sink    classic_file_sink;
#else
    struct classic_file_source : io::source {
        classic_file_source(const std::string& path) 
            : file_(new filebuf)
        {
            file_->pubimbue(locale::classic());
            file_->open(path.c_str(), BOOST_IOS::in | BOOST_IOS::binary);
        }
        streamsize read(char* s, streamsize n) { return file_->sgetn(s, n); }
        boost::shared_ptr<filebuf> file_;
    };

    struct classic_file_sink : io::sink  {
        classic_file_sink(const std::string& path) 
            : file_(new filebuf)
        {
            file_->pubimbue(locale::classic());
            file_->open(path.c_str(), BOOST_IOS::out | BOOST_IOS::binary);
        }
        streamsize write(const char* s, streamsize n) { return file_->sputn(s, n); }
        boost::shared_ptr<filebuf> file_;
    };
#endif

template<typename Codecvt>
bool codecvt_test1()
{
    typedef basic_string<
                BOOST_DEDUCED_TYPENAME 
                codecvt_intern<Codecvt>::type
            >                                             string_type;
    typedef code_converter<classic_file_source, Codecvt>  wide_file_source;
    typedef code_converter<classic_file_sink, Codecvt>    wide_file_sink;

    BOOST_CHECK(Codecvt().max_length() <= max_length);
    temp_file                        temp;
    string_type                      test = test_string<Codecvt>();
    stream<wide_file_sink>    out(temp.name());
    out.write(test.data(), static_cast<streamsize>(test.size()));
    out.close();

    stream<wide_file_source>  in(temp.name());
    string_type                      test2;
    io::copy(in, io::back_inserter(test2));

    return test == test2;
}

template<typename Codecvt>
bool codecvt_test2()
{
    typedef basic_string<
                BOOST_DEDUCED_TYPENAME 
                codecvt_intern<Codecvt>::type
            >                                    string_type;
    typedef code_converter<classic_file_source>  wide_file_source;
    typedef code_converter<classic_file_sink>    wide_file_sink;

    // Set global locale.
    locale loc = add_facet(locale(), new Codecvt);
    locale::global(loc);

    temp_file                        temp;
    string_type                      test = test_string<Codecvt>();
    stream<wide_file_sink>    out(temp.name());
    out.write(test.data(), static_cast<streamsize>(test.size()));
    out.close();

    stream<wide_file_source>  in(temp.name());
    string_type                      test2;
    io::copy(in, io::back_inserter(test2));

    return test == test2;
}

template<typename Codecvt>
bool codecvt_test()
{ 
    return codecvt_test1<Codecvt>() && codecvt_test2<Codecvt>(); 
}

void code_converter_test()
{
    BOOST_CHECK((codecvt_test<utf8_codecvt_facet<wchar_t, char> >()));
    BOOST_CHECK(codecvt_test<null_padded_codecvt>());
    BOOST_CHECK(codecvt_test<stateless_null_padded_codecvt>());
#ifdef BOOST_IOSTREAMS_USE_DINKUM_COREX
    using namespace Dinkum::conversions;
    BOOST_CHECK(codecvt_test< codecvt_8859_1<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_8859_10<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_8859_13<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_8859_14<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_8859_15<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_8859_16<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_8859_2<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_8859_3<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_8859_4<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_8859_5<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_8859_6<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_8859_7<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_8859_8<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_8859_9<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_baltic<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_big5<wchar_t> >());   
    BOOST_CHECK(codecvt_test< codecvt_cp037<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_cp1006<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1026<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1250<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1251<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1252<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1253<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1254<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1255<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1256<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1257<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp1258<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp424<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_cp437<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_cp500<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp737<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp775<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp850<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp852<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp855<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp856<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp857<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp860<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp861<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp862<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp863<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp864<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp865<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp866<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp869<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp874<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp875<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp932<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp936<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_cp949<wchar_t> >());    
    BOOST_CHECK(codecvt_test< codecvt_cp950<wchar_t> >());    
    BOOST_CHECK(codecvt_test< codecvt_cyrillic<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_ebcdic<wchar_t> >());   
    BOOST_CHECK(codecvt_test< codecvt_euc<wchar_t> >());      
    BOOST_CHECK(codecvt_test< codecvt_euc_0208<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_gb12345<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_gb2312<wchar_t> >());   
    BOOST_CHECK(codecvt_test< codecvt_greek<wchar_t> >());    
    BOOST_CHECK(codecvt_test< codecvt_iceland<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_jis<wchar_t> >());       
    BOOST_CHECK(codecvt_test< codecvt_jis_0208<wchar_t> >());  
    BOOST_CHECK(codecvt_test< codecvt_jis0201<wchar_t> >());   
    BOOST_CHECK(codecvt_test< codecvt_ksc5601<wchar_t> >());   
    BOOST_CHECK(codecvt_test< codecvt_latin2<wchar_t> >());    
    BOOST_CHECK(codecvt_test< codecvt_one_one<wchar_t> >());   
    BOOST_CHECK(codecvt_test< codecvt_roman<wchar_t> >());     
    BOOST_CHECK(codecvt_test< codecvt_sjis<wchar_t> >());      
    BOOST_CHECK(codecvt_test< codecvt_sjis_0208<wchar_t> >()); 
    BOOST_CHECK(codecvt_test< codecvt_turkish<wchar_t> >());
    BOOST_CHECK(codecvt_test< codecvt_utf16<wchar_t> >());
    BOOST_CHECK(codecvt_test< codecvt_utf8<wchar_t> >());
    BOOST_CHECK(codecvt_test< codecvt_utf8_utf16<wchar_t> >());
#endif
}

test_suite* init_unit_test_suite(int, char* []) 
{
    test_suite* test = BOOST_TEST_SUITE("code_converter test");
    test->add(BOOST_TEST_CASE(&code_converter_test));
    return test;
}