/******************************************************************************
 *
 * Project:  OpenCPN
 * Purpose:  OpenCPN Route table printout
 * Author:   Pavel Saviankou
 *
 ***************************************************************************
 *   Copyright (C) 2012 by David S. Register                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
 ***************************************************************************
 *
 */
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;

#include "wx/wxprec.h"

#ifndef  WX_PRECOMP
#include "wx/wx.h"
#endif //precompiled headers
#ifdef __WXMSW__
//#include "c:\\Program Files\\visual leak detector\\include\\vld.h"
#endif

#include "wx/print.h"
#include "wx/printdlg.h"
#include "wx/artprov.h"
#include "wx/stdpaths.h"
#include <wx/intl.h>
#include <wx/listctrl.h>
#include <wx/aui/aui.h>
#include <wx/dialog.h>
#include <wx/progdlg.h>
#include <wx/brush.h>
#include <wx/colour.h>
#include <wx/tokenzr.h>

#if wxCHECK_VERSION( 2, 9, 0 )
#include <wx/dialog.h>
#else
//  #include "scrollingdialog.h"
#endif

#include "dychart.h"

#ifdef __WXMSW__
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <psapi.h>
#endif

#ifndef __WXMSW__
#include <signal.h>
#include <setjmp.h>
#endif

#include "printtable.h"

void PrintCell::Init( const wxString& _content, wxDC* _dc, int _width, int _cellpadding, bool _bold_font )
{
    bold_font   = _bold_font;
    dc          = _dc;
    width       = _width;
    cellpadding = _cellpadding;
    content     = _content;
    page        = 1;
    Adjust();
};


void PrintCell::Adjust()
{
    wxFont orig_font = dc->GetFont();
    wxFont _font = orig_font;
    if ( bold_font ) {
        _font.SetWeight( wxFONTWEIGHT_BOLD );
    }
    dc->SetFont( _font );
    vector<wxString>  list;
    list.push_back( wxString() );
    wxString separator = wxT( " " );
    wxStringTokenizer tokenizer( content, separator, wxTOKEN_RET_DELIMS );
    int words_number = 0;
    while ( tokenizer.HasMoreTokens() ) {
        wxString token = tokenizer.GetNextToken();
        wxCoord h = 0;
        wxCoord w = 0;
        wxString tmp = list[ list.size() - 1 ];
        wxString tmp2 = tmp + token;
        words_number++;
        dc->GetMultiLineTextExtent( tmp2, &w, &h );
        if ( ( w < width - 2 * cellpadding ) || words_number == 1 ) {
            list[ list.size() - 1 ] = tmp2;
        } else{
            list.push_back( wxString() );
        }
    }

    for ( size_t i = 0; i < list.size() - 1; i++ ) {
        modified_content = modified_content + list[ i ] + _T('\n');
    }
    // now add last element without new line
    modified_content = modified_content + list[ list.size() - 1 ];

    wxCoord h = 0;
    wxCoord w = 0;
    dc->GetMultiLineTextExtent( modified_content, &w, &h );
    SetHeight( h + 8);

    dc->SetFont( orig_font );
}


Table::Table()
{
    nrows = 0;
    ncols = 0;
    data.clear();
    state = TABLE_SETUP_WIDTHS;
    create_next_row = true;
}


Table::~Table()
{
    for ( vector < vector < wxString > >::iterator iter = data.begin(); iter != data.end(); iter++ ) {
        ( *iter ).clear();
    }
    data.clear();
}


void Table::Start()
{
    if ( create_next_row ) {
        NewRow();
        create_next_row = false;
    }
}


void Table::NewRow()
{
    vector<wxString> empty_row;
    data.push_back( empty_row );
}


Table& Table::operator<<( const double& cellcontent )
{
    if ( state == TABLE_SETUP_WIDTHS ) {
        widths.push_back( cellcontent );
        return *this;
    }
    if ( state == TABLE_FILL_DATA ) {
        stringstream sstr;
        sstr << cellcontent;
        string _cellcontent = sstr.str();
        Start();
        wxString _str( _cellcontent.c_str(), wxConvUTF8 );
        data[ data.size() - 1 ].push_back( _str );
    }
    return *this;
}


Table& Table::operator<<( const wxString& cellcontent )
{
    Start();
    if ( state == TABLE_FILL_HEADER ) { // if we start to fill with string data, we change state automatically.
        header.push_back( cellcontent );
        return *this;
    }
    if ( state == TABLE_SETUP_WIDTHS ) { // if we start to fill with string data, we change state automatically.
        state = TABLE_FILL_DATA;
    }

    if ( ( cellcontent == "\n" ) ) {
        create_next_row = true;
        return *this;
    }
    data[ data.size() - 1 ].push_back( cellcontent );
    return *this;
}




Table& Table::operator<<( const int& cellcontent )
{
    if ( state == TABLE_SETUP_WIDTHS ) {
        widths.push_back( ( double )cellcontent );
        return *this;
    }
    if ( state == TABLE_FILL_DATA ) {
        stringstream sstr;
        sstr << cellcontent;
        string _cellcontent = sstr.str();
        Start();
        wxString _str( _cellcontent.c_str(), wxConvUTF8 );
        data[ data.size() - 1 ].push_back( _str );
    }
    return *this;
}


ostream& operator<<( ostream& out, Table& table )
{
    vector< vector < wxString > > & data = table.GetData();

    for ( vector < vector < wxString > >::iterator iter = data.begin(); iter != data.end(); iter++ ) {
        vector< wxString > row = ( *iter );
        for ( vector < wxString >::iterator rowiter = row.begin(); rowiter != row.end(); rowiter++ ) {
            out << ( *rowiter ).fn_str() << " ";
        }
        out << endl;
    }
    return out;
}


PrintTable::PrintTable() : Table()
{
    rows_heights.clear();
}


void PrintTable::AdjustCells( wxDC* dc, int marginX, int marginY )
{
    number_of_pages = -1;
    contents.clear();
    int sum = 0;
    for ( size_t j = 0; j < widths.size(); j++ ) {
        sum += widths[ j ];
    }

    int w, h;
    dc->GetSize( &w, &h );
    
    double scale_x, scale_y;
    dc->GetUserScale(&scale_x, &scale_y);
    w /= scale_x;
    h /= scale_y;
    
    int width = w - 4 * marginX;
    header_height = -1;
    for ( size_t j = 0; j < header.size(); j++ ) {
        int cell_width = ( int )( ( double )width * widths[ j ] / sum );
        PrintCell cell_content;
        cell_content.Init( header[ j ], dc, cell_width, 10, true );
        header_content.push_back( cell_content );
        header_height = std::max( header_height, cell_content.GetHeight() );
    }

    for ( size_t i = 0; i < data.size(); i++ ) {
        vector<wxString> row = data[ i ];
        vector<PrintCell> contents_row;
        int max_height = -1;
        for ( size_t j = 0; j < row.size(); j++ ) {
            int cell_width = ( int )( ( double )width * widths[ j ] / sum );
            PrintCell cell_content;
            cell_content.Init( row[ j ], dc, cell_width, 10 );
            contents_row.push_back( cell_content );
            max_height = std::max( max_height, cell_content.GetHeight() );
        }
        rows_heights.push_back( max_height );
        contents.push_back( contents_row );
    }

    int stripped_page = h - 4 * marginY - header_height;
    int current_page = 1;
    int current_y = 0;
    for ( size_t i = 0; i < data.size(); i++ ) {
        int row_height = rows_heights[ i ];
        if ( row_height + current_y > stripped_page ) {
            current_page++;
            current_y = row_height;
        } else {
            current_y += row_height;
        }
        int row_page = current_page;
        vector<PrintCell> & contents_row = contents[ i ];
        for ( size_t j = 0; j < contents_row.size(); j++ ) {
            contents_row[ j ].SetPage( row_page );
            contents_row[ j ].SetHeight( row_height );
        }
        number_of_pages = std::max( row_page, number_of_pages );
    }
}
