#pragma once
#include <wx/wx.h>
#include "utils/WaitSample.h"
#include "wx/popupwin.h"
#include "wx/dataview.h"
#include "wx/headerctrl.h"
#include <wx/tokenzr.h>
#include <vector>
//#include "utils/misc.h"
// ----------------------------------------------------------------------------
// MyCustomRendererText
// ----------------------------------------------------------------------------
extern std::vector<int> sort_vec_map(const std::vector<int>& src, int& sum);
#define sepListWait ";"
extern int s_pid_HIGHLIGH;
class MyCustomRendererGraph : public wxDataViewCustomRenderer
{
public:
    // This renderer can be either activatable or editable, for demonstration
    // purposes. In real programs, you should select whether the user should be
    // able to activate or edit the cell and it doesn't make sense to switch
    // between the two -- but this is just an example, so it doesn't stop us.
    explicit MyCustomRendererGraph(wxDataViewCellMode mode, int column)
        : wxDataViewCustomRenderer("string", mode, wxALIGN_LEFT)
    {
        EnableEllipsize(wxELLIPSIZE_END);
        col = column;
    }

    virtual bool Render(wxRect rect, wxDC* dc, int state) wxOVERRIDE
    {
        wxString l;
        wxRect orig = rect;
        if (col < 2) {
            l = m_value;
            if (m_value == "ffffffffffffffff" || m_value == "-1") {
                l = "SUM";
                RenderText(l, 0, rect, dc, state);
                return true;
            }
        }

        if (col == 0) {
            if (m_value[0] == '*') {
                l = m_value.AfterFirst('*');

                // wxFont fn = dc->GetFont().Italic();
                // wxDCFontChanger nfont(*dc, fn);
                dc->SetBrush(*wxLIGHT_GREY_BRUSH);
                dc->SetPen(*wxTRANSPARENT_PEN);
                rect.Deflate(1);
                dc->DrawRoundedRectangle(rect, 3);
                RenderText(l, 0, rect, dc, state);
            }
            else
            {
                l = m_value;
                RenderText(l, 0, rect, dc, state);
            }

            return true;

        }
        if (col == 1) {
            RenderText(l, 0, rect, dc, state);
            return true;
        }
        //dc->SetBrush( *wxLIGHT_GREY_BRUSH );
        dc->SetBrush(*wxYELLOW_BRUSH);
        //dc->SetPen(*wxTRANSPARENT_PEN);
        dc->SetPen(*wxBLACK_PEN);
        rect.Deflate(1);
        //WaitSample*ws= static_cast<topDataViewCtrl*>(GetView())->GetSample();
        //dc->DrawRoundedRectangle( rect, 3 );
        wxFont fn = dc->GetFont();
        fn.SetPointSize(fn.GetPointSize() - 1);
        wxDCFontChanger nfont(*dc, fn);

        wxString s = m_value, t;
        wxStringTokenizer tk(s, sepListWait, wxTOKEN_DEFAULT);

        bool isDis = true;
        bool isGrp = true;
        int f = 0;
        float itog = 0;
        int widt = orig.width - 20;
        wxPoint p(orig.x, orig.y);
        while (tk.HasMoreTokens())
        {
            l = tk.GetNextToken();
            if (f == 0) {
                float tmp;
                l = l.AfterLast(' ');
                wxSscanf(l, "%f", &tmp);
                itog = tmp;
                float sek = itog / 1000;
                wxString tt = wxString::Format("%.1f", sek); // time sec
                wxSize sz = GetTextExtent(tt);
                wxRect drw(orig.x + orig.width - sz.x - 2, orig.y, sz.x + 2, orig.GetHeight());
                wxRect npos(0, 0, sz.x, sz.y);
                npos = npos.CentreIn(drw);
                dc->DrawText(tt, npos.x, npos.y);
                widt = orig.width - sz.x - 2;
            }
            if (f > 0) {
                float tmp;
                wxString w;
                long cl;
                l = l.AfterFirst(' ');
                wxSscanf(l, "%ld %f", &cl, &tmp);

                wxColour c(cl);
                //c.Set(cl);
                dc->SetBrush(c);


                int ww = widt * tmp;
                wxRect drw(p.x, p.y, ww, rect.GetHeight());
                float sek = itog * tmp / 1000;
                wxString tt = wxString::Format("%.1f", sek); // time sec
                wxSize sz = GetTextExtent(tt);
                wxRect npos(0, 0, sz.x, sz.y);
                npos = npos.CentreIn(drw);
                dc->DrawRectangle(drw);
                {
                    wxColour fg(ContrastColorBlackOrWhite(c));
                    wxDCTextColourChanger setfg(*dc, fg);
                    if (sz.GetWidth() < drw.GetWidth()) dc->DrawText(tt, npos.x, npos.y);
                }
                p.x = p.x + ww;

            }
            f++;

        }

        return true;
    }

    virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
        wxDataViewModel* WXUNUSED(model),
        const wxDataViewItem& WXUNUSED(item),
        unsigned int WXUNUSED(col),
        const wxMouseEvent* mouseEvent) wxOVERRIDE
    {
        return false;
    }

    virtual wxSize GetSize() const wxOVERRIDE
    {
        wxSize txtSize = GetTextExtent(m_value);
        int lines = m_value.Freq('\n') + 1;
        if (lines > 1) {
            // wxLogMessage("MyCustomRendererText GetSize() %s", position);
            txtSize.SetHeight(txtSize.GetHeight() * lines + 1 * lines);
        }
        else {
#ifdef __WXGTK__
            txtSize.SetHeight(txtSize.GetHeight() + 3);
#else
            txtSize.SetHeight(-1);
#endif


        }
        if (col == 2) txtSize.SetWidth(-1);
        else
        {
            //maxw = txtSize.GetWidth();
            //maxw=wxMax(txtSize.GetWidth(), maxw);
            txtSize.SetWidth(txtSize.GetWidth() + 1);
        }
        return txtSize;

        //return GetView()->FromDIP(wxSize(60, 20));
    }

    virtual bool SetValue(const wxVariant& value) wxOVERRIDE
    {
        m_value = value.GetString();
        return true;
    }

    virtual bool GetValue(wxVariant& WXUNUSED(value)) const wxOVERRIDE { return true; }

#if wxUSE_ACCESSIBILITY
    virtual wxString GetAccessibleDescription() const wxOVERRIDE
    {
        return m_value;
    }
#endif // wxUSE_ACCESSIBILITY

    virtual bool HasEditorCtrl() const wxOVERRIDE { return false; }
private:
    wxString m_value;
    int col;
    int maxw = -1;
};

class MyIndexListModel : public wxDataViewIndexListModel
{
public:
    MyIndexListModel(WaitSample* w) { ws = w; }

    void SetAllRows(const std::vector<key3>& keys, const std::map<key3, vec_int>& wait, bool isfp) {
        k = keys;
        w = wait;
        //IsFirstPid = isfp;

        Reset(keys.size());

    }
    key3 GetRowValue(long row) {
        key3 r{};
        if (row >= 0 && row < k.size()) {
            r = k[row];
        }
        return r;
    }
    // Implement base class pure virtual methods.
    unsigned GetCount() const wxOVERRIDE { return k.size(); }
    void GetValueByRow(wxVariant& val, unsigned row, unsigned col) const wxOVERRIDE
    {
        //val=wxString::Format("r:%d,c:%d",row,col);
        //val = m_strings[row];
        //if (row + 1 < m_strings.Count() && col == 1 ) val = m_strings[row + 1];
        bool ishint = false;
        int r = row;
        if (!val.IsNull()) {
            //r = r * -1;
            ishint = true;
        }
        if (k.size() > 0) {
            key3 kk = k.at(r);
            if (col == 0) {
                long ttmp = kk.pid;
                if (kk.sum > 0) val = wxString::Format("*%ld", ttmp); // backend
                else
                    val = wxString::Format("%ld", ttmp);

            }
            else
                val = wxString::Format("%llx", kk.qid);
            if (col == 2) {
                vec_int v = w.at(kk);
                int sz = v.size();
                val = wxString::Format("count w=%d", sz);
                int itog = 0;
                std::vector<int> map_sum_all = sort_vec_map(v, itog);
                int period = ws->getPeriod();
                int total = itog;
                wxString h;
                if (total != 0) {
                    wxString x = wxString::Format("all %ld", (long)itog * period);
                    for (int i = 0; i < map_sum_all.size(); i++) {
                        int wait_index = map_sum_all[i];
                        if (v[wait_index] == 0) continue;
                        float w_sum = (v[wait_index] / (float)total);
                        float sek = ((period * v[wait_index]) / 1000.0);
                        wxString w_name = ws->GetName(wait_index, WAIT_NAME);
                        wxString w_grp = ws->GetName(wait_index, WAIT_GRP);
                        wxString full = ws->GetName(wait_index, WAIT_FULL);
                        long clr = ws->GetColorByWaitName(full);
                        wxString pr = wxString::Format("%s %ld %.3f", w_name, clr, w_sum);
                        if (ishint) {
                            if (!h.IsEmpty()) h += "\n";
                            h.Append(wxString::Format("%-8.1f%-15s", sek, w_name));
                        }
                        else {
                            if (!x.IsEmpty()) x += sepListWait;
                            x += pr;
                        }
                    }
                    if (ishint) val = h;
                    else
                        val = x;
                }

            }
        }
    }
    bool SetValueByRow(const wxVariant&, unsigned, unsigned) wxOVERRIDE
    {
        return false;
    }
    unsigned int GetRow(const wxDataViewItem& item) const wxOVERRIDE {
         unsigned int i;
//         wxUIntToPtr()
         i=wxPtrToUInt(item.GetID());
         i=i-1;
         return i;
         } // 0 row = header 
    unsigned int GetColumnCount() const wxOVERRIDE { return 0; }
    wxString GetColumnType(unsigned int) const wxOVERRIDE { return ""; }


private:
    bool IsFirstPid = true;
    std::vector<key3> k;
    std::map<key3, vec_int> w;
    WaitSample* ws = NULL;
    wxDECLARE_NO_COPY_CLASS(MyIndexListModel);
};

//----------------------------------------------------------------------------
// SimpleTransientPopup
//----------------------------------------------------------------------------
class wxTopActivity;
class topDataViewCtrl;

class SimpleTransientPopup : public wxFrame
{
public:
    friend class wxTopActivity;
    friend class topDataViewCtrl;
    SimpleTransientPopup(wxWindow* parent, bool scrolled, wxTopActivity* small_ctl, wxPoint p, wxString title);
    virtual ~SimpleTransientPopup();

    // wxPopupTransientWindow virtual methods are all overridden to log them

private:
    //wxScrolledWindow* m_panel;
    wxPanel* m_panel;
    wxTopActivity* top;
    topDataViewCtrl* dvc;
    wxObjectDataPtr<MyIndexListModel> m_index_list_model;
    wxPoint mouseDownPos_;
    bool dragg = false;
    wxTimer m_taskTimer;
private:
    void OnMouse(wxMouseEvent& event);
    void OnSize(wxSizeEvent& event);
    void OnSetFocus(wxFocusEvent& event);
    void OnKillFocus(wxFocusEvent& event);
    void OnTimerEvent(wxTimerEvent& pEvent);
    void OnClose(wxCommandEvent& event);

private:
    wxDECLARE_ABSTRACT_CLASS(SimpleTransientPopup);
    wxDECLARE_EVENT_TABLE();
};

struct title_info {
    bool iner = false;
    bool remove;
    bool down_line;
    int total;
    wxString title;

};
/// <summary>
/// Графический элемент отображающий данные WaitSample
/// Может использоваться в двух режимах.
/// </summary>
class wxTopActivity : public wxControl
{
    WaitSample* ws;
    wxString m_title;
    wxSize setsize;
    wxArrayString seriosName;
    std::vector<wxDateTime> xAxis;
    std::vector<wxTimeSpan> yAxis;
    std::vector<vec_int> val;
    std::vector<long> colors;
    std::vector<title_info> m_title_i;
    std::map<wxString, int> m_collapse;
    std::map<wxString, int> m_sumtotal;
    wxPoint mouse, mouseS;
    wxRect m_area;
    int m_click = 0;
    int m_fix_detail_idx = -1;
    // fix sel Range
    wxDateTime fix_pos_L;
    wxDateTime fix_pos_R;

    wxPoint m_fix_pos; // context win
    wxString m_filter_detail;
    long long m_qid_filter = -1;
    bool m_regroup = false;
    int m_lastInterval = 0;
    int m_RightTime = -1;
    wxString m_filter;
    int m_agg_int = 5000;
    int m_count_wait;
    int m_inter[9] = {
        5000      ,// 5 sek
        10000     ,//10 sek 
        30000     ,//30 s
        60000     ,//1 min
        5 * 60000 ,//5 min
        10 * 60000,//10 min
        15 * 60000,//15 min
        30 * 60000,//30 min
        60 * 60000 //60 min
    };
    SimpleTransientPopup* m_simplePopup;
    void paintSelRange(wxDC& dc, int width_sample);
public:
    wxTopActivity(wxWindow* parent, WaitSample* WS, wxSize sz);
    wxSize DoGetBestClientSize();
    int getAggregateInterval() { return m_agg_int; };
    void setViewRange(int m_aggregate_interval, int RightTime);
    WaitSample* getViewRange(int& m_aggregate_interval, int& RightTime);
    void paintEvent(wxPaintEvent& evt);
    void OnEraseBackground(wxEraseEvent& event);
    void paintNow();
    void SetFilter(long long qid);
    /// <summary>
    /// Возвращает время указаной границы
    /// </summary>
    /// <param name="isLeftRange">true для левой границы, инаце правая</param>
    /// 
    int getTimeSelRange(bool isLeftRange);
    void render(wxDC& dc);

    // some useful events
    void mouseMoved(wxMouseEvent& event);
    void mouseDown(wxMouseEvent& event);
    void mouseWheelMoved(wxMouseEvent& event);
    void mouseReleased(wxMouseEvent& event);
    void rightClick(wxMouseEvent& event);
    void mouseLeftWindow(wxMouseEvent& event);
    void keyPressed(wxKeyEvent& event);
    void keyReleased(wxKeyEvent& event);
    void resize(wxSizeEvent& event);
    DECLARE_EVENT_TABLE()
};

class topDataViewCtrl :
    public wxDataViewCtrl
{
public:
    topDataViewCtrl(wxWindow* parent, wxWindowID id,
        const wxPoint& pos = wxDefaultPosition,
        const wxSize& size = wxDefaultSize, long style = 0,
        wxTopActivity* topactive = NULL,
        const wxValidator& validator = wxDefaultValidator,
        const wxString& name = wxASCII_STR(wxDataViewCtrlNameStr)
    ) : wxDataViewCtrl(parent, id, pos, size, style, validator, name)
    {
        //SetMinSize(wxSize(300,200));
        top = topactive;
        w = NULL;
        m_win_s = NULL;
        w = top->getViewRange(agg, right_g);
        Bind(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, [&](wxDataViewEvent& event) {
            int col = event.GetColumn();
            if (GetColumn(col)->GetTitle() == "PID") {
                ignoreBG = !ignoreBG;
                CalcRowsDataView(true, ispidfirst);
                Refresh();

            }
            });
        Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [&](wxDataViewEvent& event) {
            int col = event.GetColumn();
            wxDataViewItem item(event.GetItem());
            long row = wxPtrToUInt(item.GetID());
            row--;
            if (col == 1) //qid
            {
                MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
                key3 k = m->GetRowValue(row);
                if (k.qid != -1) top->SetFilter(k.qid);
            }
            if (col == 0) { //pid
                MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
                key3 k = m->GetRowValue(row);
                if (k.pid > 0) s_pid_HIGHLIGH = k.pid;
                //  else
                //      s_pid_HIGHLIGH = -1;
            }
            });

        Bind(wxEVT_DATAVIEW_COLUMN_REORDERED, [&](wxDataViewEvent& event) {
            wxDataViewColumn* const col = event.GetDataViewColumn();
            if (!col)
            {
                return;
            }

            ispidfirst = col->GetTitle() == "PID" && event.GetColumn() == 0;
            if (!ispidfirst) ispidfirst = col->GetTitle() == "QID" && event.GetColumn() == 1;
            CalcRowsDataView(true, ispidfirst);
            Refresh();
            if (m_win_s != NULL)  m_win_s->Refresh();
            });
        Bind(wxEVT_CHAR, [&](wxKeyEvent& event) {
            bool fnd = false;
            wxChar charcode = event.GetUnicodeKey();
            int l = m_find.length();
            if (event.GetKeyCode() == WXK_ESCAPE) {
                //GetParent()->Close(true);
                m_find = "";
                if (m_win_s != NULL) {
                    m_win_s->Destroy();
                    m_win_s = NULL;
                }

            }
            else
                if (event.GetKeyCode() == WXK_F3) {
                    //GetParent()->Close(true);
                    fnd = true;

                }
                else
                    if (event.GetKeyCode() == WXK_BACK) {

                        if (l > 0) m_find.RemoveLast();
                        //m_win_s->SetValue(m_find);
                        fnd = true;
                    }
                    else
                        if (wxIsprint(charcode))
                        {
                            //txt->EmulateKeyPress(event);
                            m_find += charcode;

                            fnd = true;
                        }
            //else
            //    event.Skip();
            if (fnd) {
                if (m_win_s == NULL && m_find.length() > 0) {
                    wxRect r;
                    r.width = GetColumn(0)->GetWidth();
#ifdef __WXGTK__
                    r.height = GTKGetUniformRowHeight();
#endif
                    wxWindow* t = this;
#ifdef WIN32
                    //wxWindow* t = this->GenericGetHeader();
                    r.height = this->GenericGetHeader()->GetSize().GetHeight();
#else
#endif
                    m_win_s = new wxTextCtrl(t, wxID_ANY, "",
                        r.GetPosition(),
                        r.GetSize(),
                        wxTE_PROCESS_ENTER
                        | wxTE_READONLY
                    );

                    m_win_s->SetInsertionPointEnd();
                }
                if (m_win_s != NULL) {
                    m_win_s->SetValue(m_find); m_win_s->Refresh();
                }
                if (m_find.length() > 0) {

                    MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
                    wxDataViewItem item(GetCurrentItem());
                    long row = wxPtrToUInt(item.GetID());
                    if (!(row < m->GetCount())) row = 0;

                    while (row != -1 && row < m->GetCount()) {
                        key3 k = m->GetRowValue(row);

                        wxString v = wxString::Format("%d,%llx", k.pid, k.qid);
                        // m->GetValueByRow(v, row, 0);
                        bool isfind = v.Contains(m_find);
                        if (isfind) {
                            wxDataViewItem it = wxDataViewItem(wxUIntToPtr(row + 1));
                            SetCurrentItem(it);
                            EnsureVisible(it);
                            return;
                        }
                        row++;
                    }
                }
                return;
            }
            // end function
            event.Skip();
            });
        GetMainWindow()->Bind(wxEVT_MOTION, [&](wxMouseEvent& event) {
            if (event.Dragging())
                return;

            wxClientDC dc(GetMainWindow());
            PrepareDC(dc);

            //wxPoint logPos(event.GetLogicalPosition(dc));
            //wxPoint mc= GetMainWindow()->ScreenToClient(event.GetPosition());
            wxPoint mc = event.GetPosition();
            wxString position;
            //position = wxString::Format("x=%d y=%d", logPos.x, logPos.y);
            //wxLogMessage("Mouse pos %s", position);
            int dy = 0;
            wxSize sz;
#ifdef WIN32
            wxHeaderCtrl* const header = GenericGetHeader();
            if (header) {
                sz = header->GetSize();
                //header->Refresh();
                dy = sz.GetHeight();
            }
            mc.y += dy;
#else
            mc.y += 18; // only linux compile
#endif
            wxDataViewItem item;
            wxDataViewColumn* column;

            HitTest(mc, item, column);
            if (item != NULL && column != NULL)
            {
                int row = wxPtrToUInt(item.GetID()) - 1;
                int ncol = column->GetModelColumn();
                if (row >= 0) {
                    if (column && column->GetModelColumn() == 2) {
                        wxVariant vr;
                        vr = "gethint";
                        if ((lastcol != ncol) || (lastrow != row)) {
                            MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
                            m->GetValueByRow(vr, row, ncol);
                            GetMainWindow()->SetToolTip(vr.GetString());
                            lastrow = row;
                            lastcol = ncol;
                        }

                    }
                    else if (column && column->GetModelColumn() == 0) {
                        // PID
                        MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
                        key3 k = m->GetRowValue(row);
                        if (k.sum > 0) {
                            if (((lastcol != ncol) || (lastrow != row))) {
                                wxString s = w->GetBackendTypeName(k.sum);
                                GetMainWindow()->SetToolTip(s);
                                lastrow = row;
                                lastcol = ncol;

                            }
                        }
                        else
                           {
                            GetMainWindow()->UnsetToolTip(); lastcol = -1;
                        }
                    }
                    else if (column && column->GetModelColumn() == 1) {
                        //qid
                        MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
                        key3 k = m->GetRowValue(row);
                        if (k.qid != 0) {
                            if (((lastcol != ncol) || (lastrow != row))) {
                                wxString s = w->GetQueryByQid(k.qid);
                                if (s.IsEmpty()) s = "not found sql text";
                                GetMainWindow()->SetToolTip(s);
                                lastrow = row;
                                lastcol = ncol;

                            }
                        }
                        else
                        {
                            GetMainWindow()->UnsetToolTip(); lastcol = -1; lastrow = -1;
                        }

                    }
                    else
                    {
                        GetMainWindow()->UnsetToolTip();
                        lastcol = -1;
                    }
                }
            }
            else
            {
                GetMainWindow()->UnsetToolTip();
                lastcol = -1; ; lastrow = -1;
            }

            });

    }
    WaitSample* GetSample() { return w; }
    void BuildColumn(int mode);
    bool CalcRowsDataView(bool force, bool IsPidFirst);
    //void AddRow(wxString csvtext);
    //void OnMouseMove(wxMouseEvent& event);
    //void OnMouseDown(wxMouseEvent& event);
    //void OnKEY_DOWN(wxKeyEvent& event);
    //void OnKEY_UP(wxKeyEvent& event);
    //void OnEVT_DATAVIEW_COLUMN_HEADER_CLICK(wxDataViewEvent& event);
    //void OnEVT_DATAVIEW_CONTEXT_MENU(wxCommandEvent& event);
    //void OnEVT_DATAVIEW_SELECTION_CHANGED(wxDataViewEvent& event);
    //void OnContextMenu(wxDataViewEvent& event);

    DECLARE_EVENT_TABLE()


private:
    int lastrow = -1, lastcol = 1;
    int agg, right_g, left_g;
    bool ignoreBG = false;
    bool ispidfirst = true;
    wxString m_find;
    wxTextCtrl* m_win_s;
    wxTopActivity* top;
    WaitSample* w;
};

#undef sepListWait

