bool FileCSV::Load(const wxString& fileName, unsigned int itemsInLine)
{
    // Make sure file exists
    if (fileName.IsEmpty() || !wxFileName::FileExists(fileName))
    {
        mmErrorDialogs::InvalidFile(pParentWindow_);
        return false;
    }

    // Open file
    wxTextFile txtFile(fileName);
    if (!txtFile.Open(encoding_))
    {
        mmErrorDialogs::MessageError(pParentWindow_, _("Unable to open file."), _("Universal CSV Import"));
        return false;
    }

    // Parse rows
    wxString line;
    int row = 0;
    for (line = txtFile.GetFirstLine(); !txtFile.Eof(); line = txtFile.GetNextLine())
    {
        csv2tab_separated_values(line, delimiter_);
        wxStringTokenizer tkz(line, "\t", wxTOKEN_RET_EMPTY_ALL);
        itemsTable_.push_back(std::vector<ValueAndType>());

        // Tokens in row
        while (tkz.HasMoreTokens())
        {
            if (itemsTable_[row].size() >= itemsInLine)
                break;

            wxString token = tkz.GetNextToken();
            itemsTable_[row].push_back(token);
        }

        ++row;
    }

    txtFile.Close();
    return true;
}
void mmStockDialog::OnHistoryImportButton(wxCommandEvent& /*event*/)
{
    if (m_stock->SYMBOL.IsEmpty())
        return;

    bool canceledbyuser = false;
    const wxString fileName = wxFileSelector(_("Choose CSV data file to import")
        , wxEmptyString, wxEmptyString, wxEmptyString, "*.csv", wxFD_FILE_MUST_EXIST);
    Model_Account::Data *account = Model_Account::instance().get(m_stock->HELDAT);
    Model_Currency::Data *currency = Model_Account::currency(account);

    if (!fileName.IsEmpty())
    {
        wxFileName csv_file(fileName);
        if (fileName.IsEmpty() || !csv_file.FileExists())
            return;
        wxTextFile tFile(fileName);
        if (!tFile.Open())
            return;
        wxProgressDialog* progressDlg = new wxProgressDialog(_("Stock History CSV Import")
            , _("Quotes imported from CSV: "), tFile.GetLineCount()
            , NULL, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_SMOOTH | wxPD_CAN_ABORT);
        long countNumTotal = 0;
        long countImported = 0;
        double price;
        wxDateTime dt;
        wxString dateStr, priceStr;
        Model_StockHistory::Data *data;
        Model_StockHistory::Cache stockData;

        wxString line;
        std::vector<wxString> rows;
        for (line = tFile.GetFirstLine(); !tFile.Eof(); line = tFile.GetNextLine())
        {
            wxString progressMsg;
            progressMsg << _("Quotes imported from CSV: ") << countImported;
            if (!progressDlg->Update(countImported, progressMsg))
            {
                canceledbyuser = true;
                break; // abort processing
            }

            if (!line.IsEmpty())
                ++countNumTotal;
            else
                continue;

            dateStr.clear();
            priceStr.clear();

            const wxString& delimiter = Model_Infotable::instance().GetStringInfo("DELIMITER", mmex::DEFDELIMTER);
            csv2tab_separated_values(line, delimiter);
            wxStringTokenizer tkz(line, "\t", wxTOKEN_RET_EMPTY_ALL);  
            if ((int)tkz.CountTokens() < 2)
                continue;
            
            std::vector<wxString> tokens;
            while (tkz.HasMoreTokens())
            {
                wxString token = tkz.GetNextToken();
                tokens.push_back(token);
            }

            // date
            dateStr = tokens[0];
            mmParseDisplayStringToDate(dt, dateStr, mmOptions::instance().dateFormat_);
            dateStr = dt.FormatISODate();
            // price
            priceStr = tokens[1];
            priceStr.Replace(" ", wxEmptyString);
            if (!Model_Currency::fromString(priceStr, price, currency) || price <= 0.0)
                continue;

            data = Model_StockHistory::instance().create();
            data->SYMBOL = m_stock->SYMBOL;
            data->DATE = dateStr;
            data->VALUE = price;
            data->UPDTYPE = 2;
            stockData.push_back(data);

            if (rows.size()<10)
            {
                dateStr <<  wxT ("  ") << priceStr;
                rows.push_back(dateStr);
            }
            countImported++;
        }

        progressDlg->Destroy();       

        wxString msg = wxString::Format(_("Total Lines : %ld"), countNumTotal); 
        msg << "\n";
        msg << wxString::Format(_("Total Imported : %ld"), countImported); 
        msg << "\n";
        msg << _("Date") << "              " << _("Price");
        msg << "\n";
        for (std::vector<wxString>::const_iterator d = rows.begin(); d != rows.end(); ++d)
            msg << *d << "\n";
        wxString confirmMsg = msg + _("Please confirm saving...");
        if (!canceledbyuser && wxMessageBox(confirmMsg
            , _("Importing CSV"), wxOK | wxCANCEL | wxICON_INFORMATION) == wxCANCEL)
        {
            canceledbyuser = true;
        }
 
        // Since all database transactions are only in memory,
        if (!canceledbyuser)
        {
            // we need to save them to the database. 
            for (auto &d : stockData)
                Model_StockHistory::instance().save(d);
            // show the data
            showStockHistory();
        }
        else 
        {
            //TODO: and discard the database changes.
        }
    }
}