#include "pgAdmin3.h"
#include "ctl/ctlNavigatePanel.h"
#include <wx/listctrl.h>
#include <wx/dcbuffer.h>
#include <wx/debug.h>
#include "utils/json/jsonval.h"
#include "utils/csvfiles.h"
//#include <wx/window.h>

BEGIN_EVENT_TABLE(ctlNavigatePanel, wxWindow)

EVT_MOTION(ctlNavigatePanel::OnMouse)
EVT_LEFT_UP(ctlNavigatePanel::mouseReleased)
EVT_RIGHT_UP(ctlNavigatePanel::mouseReleased)
EVT_SIZE(ctlNavigatePanel::OnSize)
EVT_PAINT(ctlNavigatePanel::OnPaint)
EVT_MENU(wxID_ANY,ctlNavigatePanel::OnContextMenu)
//EVT_SET_FOCUS(ctlNavigatePanel::OnSetFocus)
//EVT_KILL_FOCUS(ctlNavigatePanel::OnKillFocus)

END_EVENT_TABLE()

void ctlNavigatePanel::Init(bool reorganization) {
    wxJSONValue def(wxJSONType::wxJSONTYPE_OBJECT);
    startdbcolor = wxColour("#f655ee");
    if (!reorganization) {
        // run construction
        def["width"] = 20;
        def["border"] = 1;
        bgcolor = *wxLIGHT_GREY;
        framecolor = *wxBLACK;
        bordercolor = *wxBLACK;
        findcolor = *wxWHITE;
        def["startdbcolor"]= startdbcolor.GetAsString(wxC2S_HTML_SYNTAX);
        def["backgroundcolor"] = bgcolor.GetAsString(wxC2S_HTML_SYNTAX);
        def["framecolor"] = framecolor.GetAsString(wxC2S_HTML_SYNTAX);
        def["bordercolor"] = bordercolor.GetAsString(wxC2S_HTML_SYNTAX);
        def["findcolor"] = findcolor.GetAsString(wxC2S_HTML_SYNTAX);
        def["contextboxsize"] = 12;
        def["findmarkwidthprocent"] = 50;
        def["findmarkwidthprocent"].AddComment("// width find market % value range 0..100 ");
        wxJSONValue cmds(wxJSONType::wxJSONTYPE_ARRAY);
        wxJSONValue cm(wxJSONType::wxJSONTYPE_OBJECT);
        cm["name"] = wxString("Find");
        cm["hotkey"] = wxString("Ctrl+F");
        cm["description"] = wxString(_("Search for a simple string."));
        cmds.Append(cm);
        wxJSONValue cm2(wxJSONType::wxJSONTYPE_OBJECT);
        cm2["name"] = wxString("GotoNextMark");
        cm2["description"] = wxString(_("Go to the next marker."));
        cm2["hotkey"] = wxString("F5");
        cmds.Append(cm2);
        wxJSONValue cm3(wxJSONType::wxJSONTYPE_OBJECT);
        cm3["name"] = wxString("GotoNextMarkEqual");
        cm3["hotkey"] = wxString("Shift+F5");
        cm3["description"] = wxString(_("Go to the next marker of the same type."));
        cmds.Append(cm3);
        wxJSONValue cm4(wxJSONType::wxJSONTYPE_OBJECT);
        cm4["name"] = wxString("FindNext");
        cm4["hotkey"] = wxString("F3");
        cm4["description"] = wxString(_("The next occurrence of the string."));
        cmds.Append(cm4);
        wxJSONValue cm5(wxJSONType::wxJSONTYPE_OBJECT);
        cm5["name"] = wxString("FindPrevious");
        cm5["hotkey"] = wxString("Shift+F3");
        cm5["description"] = wxString(_("Previous occurrence of the string."));
        cmds.Append(cm5);
        wxJSONValue cm7(wxJSONType::wxJSONTYPE_OBJECT);
        cm7["name"] = wxString("FindPid");
        cm7["hotkey"] = wxString("F7");
        cm7["description"] = wxString(_("Find all strings content current row Pid."));
        cmds.Append(cm7);
        wxJSONValue cm8(wxJSONType::wxJSONTYPE_OBJECT);
        cm8["name"] = wxString("FindState");
        cm8["hotkey"] = wxString("F8");
        cm8["description"] = wxString(_("Find all strings content current row State."));
        cmds.Append(cm8);
        wxJSONValue cm9(wxJSONType::wxJSONTYPE_OBJECT);
        cm9["name"] = wxString("AutoHint");
        cm9["hotkey"] = wxString("F9");
        cm9["description"] = wxString(_("Enable/disable auto hint."));
        cmds.Append(cm9);



        wxJSONValue cm6(wxJSONType::wxJSONTYPE_OBJECT);
        cm6["name"] = wxString(_("Help"));
        cm6["hotkey"] = wxString("F1");
        cmds.Append(cm6);

        def["commands"] = cmds;
        wxJSONValue markdef(wxJSONType::wxJSONTYPE_ARRAY);
        enum LogIndexColor : int { panic, fatal, errorxx, error, startup, logxx, notfound };
        const wxColour def_mark_color[7] = { *wxRED, wxColour(255,125,125) ,wxColour(255,213,101),  *wxYELLOW, *wxCYAN, wxColour(196,255,14), wxColour(66,76,231) };
        //                                panic    fatal ,             errorxx                   error,     startup  LOG,XX000  WARNIG,01000
        //                                red      light red           light brown               yellow     cyan     lime       blue
        for (int k = 0; k < sizeof(def_mark_color)/sizeof(def_mark_color[0]); k++) {
            wxJSONValue indicator(wxJSONType::wxJSONTYPE_OBJECT);
            indicator["color"] = def_mark_color[k].GetAsString(wxC2S_HTML_SYNTAX);
            indicator["enable"] = true;
            //indicator["regexp"] = false;
            wxJSONValue inc(wxJSONType::wxJSONTYPE_ARRAY);
            wxJSONValue exclude(wxJSONType::wxJSONTYPE_ARRAY);
            if (k == 0) {
                inc.Append(wxString(",PANIC,"));
                indicator["menulabel"] = wxString("Panic");
            }
            if (k == 1) {
                inc.Append(wxString(",FATAL,"));
                exclude.Append(wxString("/,57P03,/"));
                //indicator["regexp"] = true;
                indicator["menulabel"] = wxString("Fatal -57P03");
            }
            if (k == 2) {
                inc.Append(wxString(",ERROR,XX000,"));
                indicator["menulabel"] = wxString("ERROR,XX000");
            }
            if (k == 3) {
                inc.Append(wxString(",ERROR,"));
                exclude.Append(wxString(",25P02,"));
                indicator["menulabel"] = wxString("ERROR -25P02");
            }
            if (k == 4) {
                inc.Append(wxString(",\"startup\","));
                inc.Append(wxString(",\"postmaster\","));
                indicator["menulabel"] = wxString("Start/Shutdown");
            }
            if (k == 5) {
                inc.Append(wxString("LOG,XX0"));
                exclude.Append(wxString(" Connection timed out"));
                indicator["menulabel"] = wxString("LOG,XX0");
            }
            if (k == 6) {
                inc.Append(wxString("WARNING,01000"));
                indicator["menulabel"] = wxString("WARNING,01000");
            }
            
            indicator["include"] = inc;
            indicator["exclude"] = exclude;
            markdef.Append(indicator);
        }
        markdef.AddComment("/*\ninclude - any string match\nexclude - excluding these string\n*/");
        def["indicators"] = markdef;
        settings->ReadJsonObect("LogNavigatePanel", opt, def);

        //    settings->WriteJsonFile();
        if (!opt.IsNull()) {
            width = opt["width"].AsInt32();
            border = opt["border"].AsInt32();
            wxString strcolor = opt["backgroundcolor"].AsString();
            wxColour cc(strcolor);
            if (cc.IsOk()) bgcolor = cc;
            strcolor = opt["framecolor"].AsString();
            wxColour cc2(strcolor);
            if (cc2.IsOk()) framecolor = cc2;
            strcolor = opt["bordercolor"].AsString();
            wxColour cc3(strcolor);
            if (cc3.IsOk()) bordercolor = cc3;
            strcolor = opt["findcolor"].AsString();
            wxColour cc4(strcolor);
            if (cc4.IsOk()) findcolor = cc4;
            strcolor=opt["startdbcolor"].AsString();
            if (strcolor != "null") {
                wxColour cc4(strcolor);
                if (cc4.IsOk()) startdbcolor = cc4;

            }
            int pr = opt["findmarkwidthprocent"].AsInt32();
            if (pr >= 0 && pr <= 100) {
                findwidth = pr;
            }
            // check 
            wxJSONValue r=opt["commands"];
            bool isok=false;
            for (int j = 0; j < r.Size(); j++) {
                wxJSONValue ccm = r[j];
                if (ccm["name"].AsString()=="AutoHint") {
                    isok=true;
                    break;
                };
            }
            if (!isok) {
                opt["commands"].Append(cm9);
                settings->WriteJsonObect("LogNavigatePanel", opt);
            }
        }
        else opt = def;
    }

    wxJSONValue r = opt["indicators"];
    if (!reorganization) mark_color.clear();
    for (int i = 0; i < regExArray.size(); i++) 
        if (!regExArray[i]) delete regExArray[i];
    regExArray.clear();
    search_rule.clear();
    if (!r.IsNull()) {
        int idx = 0;
        int numIndicator = 0;
        for (int j = 0; j < r.Size(); j++) {
            wxJSONValue indicator = r[j];
            bool enable = indicator["enable"].AsBool();
            if (true) {
                wxString strcolor = indicator["color"].AsString();
                wxColour cc(strcolor);
                statistics_mark el{0,enable,cc};
                if (!reorganization) {
                    if (cc.IsOk())
                        mark_color.push_back(el);
                    else
                        mark_color.push_back(statistics_mark { 0, false,*wxBLACK });
                }
                //search_rule
                wxJSONValue inc = indicator["include"];
                wxJSONValue exc = indicator["exclude"];
                int szi = inc.Size();
                int sze = exc.Size();
                int nextIndicatorPos = idx + szi + sze;
                int excludePos = idx + szi;
                if (sze == 0) excludePos = -1;
                //find_s ff{ str,-1,-1,j};
                if (szi > 0) {
                    for (int k = 0; k < inc.Size(); k++) {
                        wxString s = inc[k].AsString();
                        wxRegEx* reg = NULL;
                            if (s.length() > 2 && s[0] == '/' && s[s.length() - 1] == '/') {
                                    wxString s2 = s.substr(1, s.length() - 2);
                                    reg = new wxRegEx(s2, wxRE_NEWLINE);
                                    if (!reg->IsValid()) {
                                            delete reg;
                                            reg = NULL;
                                            wxLogWarning(wxString::Format("Regular expression \"%s\" error. Indicator name=%s\n", s2, indicator["menulabel"].AsString()));
                                    }
                                    regExArray.push_back(reg);
                            }
                            else regExArray.push_back(reg);
                        search_rule.push_back({ s, enable ? nextIndicatorPos: -1,excludePos,numIndicator });
                    }
                    // exclude
                    for (int k = 0; k < exc.Size(); k++) {
                        wxString s = exc[k].AsString();
                        wxRegEx* reg = NULL;
                        if (s.length() > 2 && s[0] == '/' && s[s.length() - 1] == '/') {
                            wxString s2 = s.substr(1, s.length() - 2);
                            reg = new wxRegEx(s2, wxRE_NEWLINE);
                            if (!reg->IsValid()) {
                                delete reg;
                                reg = NULL;
                                wxLogWarning(wxString::Format("Regular expression \"%s\" error. Indicator name=%s\n", s2, indicator["menulabel"].AsString()));
                            }
                            regExArray.push_back(reg);
                        }
                        else regExArray.push_back(reg);
                        search_rule.push_back({ s,enable ? nextIndicatorPos : -1,nextIndicatorPos,numIndicator });
                    }
                    idx = nextIndicatorPos;
                }
                numIndicator++;
            }
        }
    }

}
wxMenu* ctlNavigatePanel::GetPopupMenu() {
    int Idcmd=MNU_MARK + 500;
    
    wxJSONValue cmds = opt["commands"];
    //wxAcceleratorEntry acc(event.GetModifiers(), event.GetKeyCode());
    if (!cmds.IsNull()) {
        wxMenu* m = new wxMenu();
        for (int j = 0; j < cmds.Size(); j++) {
            wxJSONValue c = cmds[j];
            wxString hotkey = c["hotkey"].AsString();
            wxString cmdName = c["name"].AsString();
            wxString desc = c["description"].AsString();
            if (desc == "null") desc = cmdName;
            wxString menulabel = c["menulabel"].AsString();
            if (menulabel == "null") menulabel = cmdName;

            //wxAcceleratorEntry* ac = wxAcceleratorEntry::Create(cmdName + '\t' + hotkey);
            m->Append(Idcmd, menulabel + '\t' + hotkey, desc);
            Idcmd++;
        }
        return m;
    } 
    return NULL;
}
void ctlNavigatePanel::SetFindString(const wxString& findstr) {
    logFindString = findstr;
    items_find.clear();
    FindText(logFindString, FOCUSNEXT, false);
    Refresh();
}

bool ctlNavigatePanel::RunKeyCommand(wxKeyEvent& event,int numCmd) {
    //wxAcceleratorEntry::ParseAccel(const wxString & text, int* flagsOut, int* keyOut);
    wxJSONValue cmds = opt["commands"];
    wxAcceleratorEntry acc(event.GetModifiers(), event.GetKeyCode());
    if (!cmds.IsNull()) {
        wxString helpstr;
        bool help = false;
        for (int j = 0; j < cmds.Size(); j++) {
            wxJSONValue c = cmds[j];
            wxString hotkey = c["hotkey"].AsString();
            wxString cmdName = c["name"].AsString();
            wxString desc = c["description"].AsString();
            if (desc == "null") desc = cmdName;
            wxAcceleratorEntry* ac = wxAcceleratorEntry::Create(cmdName+'\t'+hotkey);
            if (ac == NULL) {
                wxMessageBox(wxString::Format(_("Incorrect hotkey \"%s\" for command \"%s\""), hotkey, cmdName));
                continue;
            }
            wxString translite=wxGetTranslation(desc);
            helpstr += wxString::Format("%s \t\t- %s\n",hotkey,translite);
            bool isok = *ac == acc;
            if (numCmd != -1) {
                isok = j == numCmd;
            }
            delete ac;
            bool shift = false;
            bool directionUp = false;
            if (isok && cmdName == "Help") help = true;
            if (isok && cmdName == "AutoHint") {
                bool curr=ctrl->ToggleAutoHint();
                return true;
            }
            
            if (isok && cmdName == "FindNext") {
                // F3
                if (logFindString.IsEmpty()) cmdName = "Find";
                else
                {
                    FindText(logFindString, FOCUSNEXT, directionUp);
                    return true;
                }
            }
            if (isok && cmdName == "FindPrevious") {
                // Shift+F3
                directionUp = !directionUp;
                if (logFindString.IsEmpty()) cmdName = "Find";
                else
                {
                    FindText(logFindString, FOCUSNEXT, directionUp);
                    return true;
                }
            }
            if (isok && cmdName=="Find") {
                // 
                wxTextEntryDialog dialog(this,
                    _("Please enter find string\n")
                    ,
                    _("Find"),
                    logFindString,
                    wxOK | wxCANCEL);		//setName( dlg.GetValue().wc_str() );
                if (dialog.ShowModal() == wxID_OK) {
                    logFindString = dialog.GetValue();
                    items_find.clear();
                    if (logFindString.IsEmpty()) {
                        Refresh();
                        return true;
                    }
                }
                else
                {
                    return true;
                }
                FindText(logFindString, FOCUSNEXT, directionUp);
                Refresh();
                return true;
            }

            if (isok && cmdName == "GotoNextMarkEqual") {
                shift = true;
                cmdName = "GotoNextMark";
            }
            if (isok && cmdName == "GotoNextMark") {
                // goto marker

                long currentrow = ctrl->GetFocusedItem();
                if (currentrow < 0) currentrow = 0;
                const wxColour currCol = ctrl->GetItemBackgroundColour(currentrow);
                int coloridx = GetIndexColor(currCol);
                if (shift && coloridx == -1 && lastUseMark != -1) coloridx = lastUseMark;
                if (!shift && coloridx == -1 && lastUseMark != -1) coloridx = lastUseMark;
                long rowmark;
                long last = -1;
                int mark_clr;
                for (int i = 0; i < GetCountMark(); i++) {
                    rowmark = GetItemMark(i);
                    mark_clr = color_items_mark[i];

                    if (rowmark <= currentrow || !mark_color[mark_clr].enable) continue;
                    last = i;
                    if (shift) {
                        //GetIndexColor(ctrl->GetItemBackgroundColour(rowmark))
                        if (coloridx >= 0 && mark_clr == coloridx) {
                            break;
                        }
                        else {
                            last = -1;
                            continue;
                        }
                    }
                    break;
                }
                if (last == -1) {
                    wxBell();
                }
                else {
                    lastUseMark = mark_clr;
                    ctrl->Focus(rowmark);
                    RowVisibleCenter(rowmark);
                }
                return true;
            }
            int ncmd = 0;
            if (isok && (cmdName == "FindPid")) ncmd = 1;
            if (isok && (cmdName == "FindState")) ncmd = 2;
            if (isok && (ncmd>0)) {
                long itemFocus = ctrl->GetFocusedItem();
                wxString str = ctrl->GetTextLong(itemFocus);
                CSVTokenizer tk(str);

                // Get the fields from the CSV log.
                wxString logTime = tk.GetNextToken();
                wxString logUser = tk.GetNextToken();
                wxString logDatabase = tk.GetNextToken();
                wxString logPid = tk.GetNextToken();
                wxString logSession;
                wxString logHost = tk.GetNextToken();       // Postgres puts port with Hostname
                logSession = tk.GetNextToken();
                wxString logLineNumber = tk.GetNextToken();
                wxString logCommandTag = tk.GetNextToken();
                wxString logSessionStart = tk.GetNextToken();
                wxString logVXid = tk.GetNextToken();
                wxString logTransaction = tk.GetNextToken();

                wxString logSeverity = tk.GetNextToken();
                wxString logState = tk.GetNextToken();
                wxString logMessage = tk.GetNextToken();
                wxString logDetail = tk.GetNextToken();
                wxString logHint = tk.GetNextToken();
                wxString logIntQuery = tk.GetNextToken();
                wxString logIntQuerypos = tk.GetNextToken();
                wxString logContext = tk.GetNextToken();
                wxString logQuery = tk.GetNextToken();
                wxString logQuerypos = tk.GetNextToken();
                wxString logLocation = tk.GetNextToken();

                wxString logAppName = tk.GetNextToken();
                wxString logBackendType = tk.GetNextToken();
                wxString logLeaderPid = tk.GetNextToken();
                wxString logQid = tk.GetNextToken();
                if (ncmd==1) logFindString = "," + logPid + ",";
                else if (ncmd==2) logFindString = "," + logState + ",";
                items_find.clear();
                FindText(logFindString, FOCUSNEXT, directionUp);
                Refresh();
                return true;
            }

        }
        if (help) {
            wxMessageBox(helpstr, _("Help"));
            return true;
        }
    }
    return false;
}
ctlNavigatePanel::~ctlNavigatePanel() {
    for (int i = 0; i < regExArray.size(); i++) if (regExArray[i]) delete regExArray[i];
}
ctlNavigatePanel::ctlNavigatePanel(wxWindow* parent, ctlListView* lst) : wxWindow(parent, wxID_ANY)
{
    settings->ReloadJsonFileIfNeed();
    Init(false);

    this->SetBackgroundStyle(wxBG_STYLE_PAINT);
    //bgColor = lst->GetBackgroundColour();
    ctrl = lst;
    ctrl->SetFocus();
    DisableFocusFromKeyboard();
//    ctrl->Bind(wxEVT_LIST_ITEM_SELECTED, [&](wxListEvent& event) {
//        Refresh();
//        });
    ctrl->Bind(wxEVT_PAINT, [&](wxPaintEvent& event) {
        long toprow = ctrl->GetTopItem();
        if (topvisible != toprow) {
            Refresh();
            topvisible = toprow;
        }
        });

    wxSize sz(width, -1);
    this->SetMinSize(sz);

}
wxSize ctlNavigatePanel::DoGetBestSize() const
{
    wxSize psz = GetParent()->GetClientSize();
    return wxSize(width, psz.GetHeight() - 2*border);
}
int ctlNavigatePanel::GetIndexColor(const wxColour &color) const {
    int idx = 0;
    int sz = mark_color.size();
    for (int i1 = 0; i1 < sz; i1++) {
        if (color.GetRGB() == mark_color[i1].color.GetRGB()) break;
        idx++;
        if (idx == sz) {
           /// wxTrap();
            return -1;
        }
    }
return idx;
}
void ctlNavigatePanel::render(wxDC& dc) {
    wxColour bg = ctrl->GetBackgroundColour();

    dc.SetBrush(bgcolor);
    // wxRect r=dc.GetWindow()->GetClientRect();
    wxRect r = GetClientRect();
    wxSize sz = r.GetSize();
    //dc.DrawRectangle(0, 0, r.width, r.height);
    {
        wxDCPenChanger npen(dc, wxPen(bordercolor, border, wxPENSTYLE_SOLID));
        dc.DrawRectangle(border/2, border/2, sz.x-border/2, sz.y-border/2);
    }

    int page = ctrl->GetCountPerPage();
    wxRect c = GetClientRect();
    int vpix = c.GetHeight() - 2*border;
    long cnt = ctrl->GetItemCount();
    float f_frame_start = 1;
    float f_frame_end = 1;
    wxPoint pp1, pp2;
    //int xr = c.x + c.GetWidth() - 2*border;
    int xr = c.GetWidth() - border;
    int xl = c.x + border;
    pp1.x = xl;
    pp2.x = xr;
    // line mark
    wxColour prevc = *wxWHITE;
    int previndex = -1;
    int prevy=-100;
    int idx = 0;
    int countmark = items_mark.size();
    {
        wxDCPenChanger npen(dc, wxPen(bordercolor, 2, wxPENSTYLE_SOLID));
        int idx_item_mark = 0;
        for (auto i : items_mark) {
            int numColor = color_items_mark[idx_item_mark++];
            if (!mark_color[numColor].enable) continue;
            f_frame_start = i * 1.0f / cnt;
            pp1.y = border + f_frame_start * vpix;
            pp2.y = pp1.y;
            pp2.x = xr;
            //wxColour cll = ctrl->GetItemBackgroundColour(i);
            wxColour cll = mark_color[numColor].color;
            bool needdraw = false;
            int newidx = 0;
            if (prevy == pp1.y) {
                newidx = numColor;
                if (newidx >= idx || newidx == -1) continue;
                needdraw = true;
                // draw only high priority
            }
            if (prevy != pp1.y || needdraw) {
                dc.SetPen(cll);
                dc.DrawLine(pp1, pp2);
                if (prevy + 1 < pp1.y) {
                    // big line 
                    dc.DrawLine(wxPoint(pp1.x, pp1.y-1), wxPoint(pp2.x, pp2.y - 1));
                }
                prevc = cll;
                prevy = pp1.y;
                if (needdraw)
                    idx = newidx;
                else
                {
                    idx = numColor;
                }
                //wxCHECK(idx!=LogIndexColor::notfound);
            }
        }
    }
    int findwidthpix = ((pp2.x - pp1.x) * findwidth) / 100;
    if (findwidth!=0 || findwidthpix!=0) {
        // find mark
        wxDCPenChanger npen(dc, wxPen(findcolor, 1, wxPENSTYLE_SOLID));


        prevy = -100;
        for (auto i : items_find) {
            f_frame_start = i * 1.0f / cnt;
            pp1.y = border + f_frame_start * vpix;
            pp2.y = pp1.y;
            pp2.x = pp1.x+ findwidthpix;
            if (prevy == pp1.y) continue;
            dc.DrawLine(pp1, pp2);
            prevy = pp1.y;
        }
    }
    if (ctrl->GetItemCount() == 0) return;
    // bracket frame 
    dc.SetPen(framecolor);
    long top = ctrl->GetTopItem();
    //if (top > 0) top--;
    if (cnt == 0) cnt = 1;
    f_frame_start = top * 1.0f / cnt;
    f_frame_end = (top + page) * 1.0f / cnt;
    // left line
    wxPoint p1(xl, border + f_frame_start * vpix);
    wxPoint p2(xl, border + f_frame_end * vpix);
    dc.DrawLine(p1, p2);
    // right line
    wxPoint p11(xr-1, border + f_frame_start * vpix);
    wxPoint p22(xr-1, border + f_frame_end * vpix);
    dc.DrawLine(p11, p22);
    //left risk
    dc.DrawLine(p1, wxPoint(p1.x + 2, p1.y));
    dc.DrawLine(p2, wxPoint(p2.x + 2, p2.y));
    // right risk
    dc.DrawLine(wxPoint(p11.x - 1, p11.y), p11);
    dc.DrawLine(wxPoint(p22.x - 1, p22.y), wxPoint(p22.x + 1, p22.y));
    // start db intervals
    {
        wxDCPenChanger npen(dc, wxPen(startdbcolor, 2, wxPENSTYLE_SOLID));
        //dc.SetPen(startdbcolor);

        for (int i = 0; i < startdbintervals.size(); i++) {
            long row = startdbintervals[i];
            // start
            f_frame_start = row * 1.0f / cnt;
            pp1.y = border + f_frame_start * vpix;
            pp1.x = xl;

            //accept
            if ((i + 1) == startdbintervals.size()) {
                row = cnt - 1; //last row
                // no accept
            }
            else
                row = startdbintervals[i + 1];
            f_frame_start = row * 1.0f / cnt;
            pp2.y = border + f_frame_start * vpix;
            pp2.x = pp1.x;
            dc.DrawLine(pp1, pp2);
            i++;
        }
    }
}
void ctlNavigatePanel::AddMarkItem(long item, int numcolor) {
    items_mark.push_back(item);
    color_items_mark.push_back(numcolor);
}
void ctlNavigatePanel::ClearMark() {
    items_mark.clear();
    color_items_mark.clear();
    items_find.clear();
    startdbintervals.clear();
    sinterval = -1; einterval = -1;
    for (auto &c : mark_color) {
        c.count = 0;
        c.enable = true;
    }
//    for (int i = 0; i < regExArray.size(); i++) if (regExArray[i]) delete regExArray[i];
//    regExArray.clear();
    topvisible = -1;
}

void ctlNavigatePanel::OnPaint(wxPaintEvent& evt) {
    wxBufferedPaintDC dc(this);
    render(dc);
}
void ctlNavigatePanel::OnSize(wxSizeEvent& evt)
{
    //wxLogMessage("%p SimpleTransientPopup::OnSize", this);
    SetSize(DoGetBestSize());
    evt.Skip();
}
int  ctlNavigatePanel::GetCountMark() {
    return items_mark.size();
}
int  ctlNavigatePanel::GetItemMark(long position) {
    if (position < items_mark.size() && position >= 0)
        return items_mark[position];
    else
        return -1;
}

int ctlNavigatePanel::binary_search(long item, const std::vector<long> &arr) {
    // binary search
    int max = arr.size() - 1;
    int min = 0;
    int i = (max - min) / 2;
    if (max == -1) i = -1;
    while (i != -1) {
        long t = arr[i];
        if (t < item) {
            min = i;
            i = min + (max - min) / 2;
        }
        else if (t > item) {
            max = i;
            i = min + (max - min) / 2;
        }
        else {
            while (i > 0 && arr[i - 1] == t) {
                i--;
            }
            break;
        }
        if (max - min < 2) {
            i = max;
            break;
        }
    }
    return i;
}
void ctlNavigatePanel::OnMouse(wxMouseEvent& evt) {
    int yy = evt.GetY() - border;
    int page = ctrl->GetCountPerPage();
    wxRect c = GetClientRect();
    int vpix = c.GetHeight() - 2*border;
    long cnt = ctrl->GetItemCount();
    float f_frame_start = 1;
    float f_frame_end = 1;
    f_frame_start = yy * 1.0f / vpix;
    long pos = cnt * f_frame_start;
    int i = binary_search(pos,items_mark);
    wxString tt;
    if (i >= 0 && i < items_mark.size() && items_mark[i] > pos) i--;
    if (i >= 0 && i < items_mark.size() ) {
        tt = ctrl->GetTextLong(items_mark[i]);
        this->SetToolTip(tt);
    }
    else
        this->UnsetToolTip();
    //return i;


}
void ctlNavigatePanel::RowVisibleCenter(long row) {
    int page = ctrl->GetCountPerPage();
    long cnt = ctrl->GetItemCount();

    int center = (row - page / 2);
    if (center < 0) center = 0;
    ctrl->EnsureVisible(row);
    long top = ctrl->GetTopItem();
    if ((row - top) < (page / 2)) ctrl->EnsureVisible(center); 
    else
        if ((row + page / 2) < cnt) ctrl->EnsureVisible(row + page / 2); 
        else 
            if (cnt - row < page) {
                ctrl->EnsureVisible(cnt - 1); // last page
                return;
            }
}
wxBitmap CreateBitmap(const wxColour& colour, bool check,int sz)
{
    if (sz <= 0 || sz > 60) sz = 15;
    wxMemoryDC dc;
    wxBitmap bmp(sz, sz);
    int w = sz, h = sz;
    dc.SelectObject(bmp);
    if (colour == wxNullColour)
        dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)));
    else
        dc.SetBrush(wxBrush(colour));
    dc.DrawRectangle(0, 0, w, h);
    if (!check) {
        wxPen p = dc.GetPen();
        wxDCPenChanger ppen(dc,wxPen(p.GetColour(),2));
        dc.DrawLine(wxPoint(0,0), wxPoint(w, h));
        dc.DrawLine(wxPoint(w, 0), wxPoint(0, h));
    }

    return bmp;
}
// ReScan List full
int ctlNavigatePanel::ReColorizeItems(int numIndictor, bool enableColor) {
    wxColour bg = ctrl->GetBackgroundColour();
    int count = 0;
//    mark_color[numIndictor].enable = enableColor;
    //items_mark.clear();
    int idx = 0;
    for (long i = 0; i < items_mark.size(); i++) {
        //int idx = TryMarkItem(i,ctrl->GetText(i));
        int item = items_mark[i];
        int numColor = color_items_mark[i];
        if (numColor == numIndictor) {
            if (enableColor)
                ctrl->SetItemBackgroundColour(item, GetColorByIndex(numColor));
            else
                ctrl->SetItemBackgroundColour(item, bg);
            count++;
        }
        //else ctrl->SetItemBackgroundColour(i, bg);
    }
    return count;
}
void ctlNavigatePanel::OnContextMenu(wxCommandEvent& event) {
    int numMark = event.GetId() - MNU_MARK;
    int numCommand = event.GetId() - MNU_MARK - 500;
    if (numCommand >= 0 && numCommand < opt["commands"].Size()) {
        // context menu command
        wxKeyEvent kevt;
        RunKeyCommand(kevt, numCommand);
        return;
    }
    if (numMark < 0|| numMark>=mark_color.size()) return;
   // wxMessageBox(wxString::Format("Num command = %d",numMark), "Command");
    // context menu marker
    bool controlkey = wxGetKeyState(WXK_CONTROL);
    if (controlkey) {
        wxJSONValue indicator = opt["indicators"][numMark];
        if (indicator["enable"].IsBool()) {
            bool en = !indicator["enable"].AsBool();
            opt["indicators"][numMark]["enable"] = en;
            wxMenuItem* mi = (wxMenuItem*)event.GetEventObject();
            mi->Check(en);
            Init(true);
            mark_color[numMark].enable = en;
            ReColorizeItems(numMark, en);
            Refresh();
        }
    }
    else {
        // goto first mark
        int idx = 0;
        for (auto i : color_items_mark) {
            if (i == numMark) {
                long item=GetItemMark(idx);
                ctrl->Focus(item);
                RowVisibleCenter(item);
                break;
            }
            idx++;
        }
    }

}
void ctlNavigatePanel::mouseReleased(wxMouseEvent& evt) {
    bool left = evt.LeftUp();
    bool right = evt.RightUp();
    bool controlkey= wxGetKeyState(WXK_CONTROL);
    if (left) {
        int yy = evt.GetY() - border;
        int page = ctrl->GetCountPerPage();
        wxRect c = GetClientRect();
        int vpix = c.GetHeight() - 2*border;
        long cnt = ctrl->GetItemCount();
        float f_frame_start = 1;
        if (cnt == 0) cnt = 1;
        f_frame_start = yy * 1.0f / vpix;
        long pos = cnt * f_frame_start;
        if (!controlkey) {
            // only left
        } else 
            if (pos < cnt) {
                    // up market find
                    int i = binary_search(pos,items_mark);
                    if (i >= 0 && i < items_mark.size() && items_mark[i] > pos) i--;
                    if (i >= 0 && i < items_mark.size()) pos = items_mark[i];
             }
        RowVisibleCenter(pos);
        Refresh();
        ctrl->SetFocus();

    }
    else {
        // right
          // Show popupmenu at position
        //menu.Append(MNU_MARK, wxT("&About"));
        
        wxJSONValue r = opt["indicators"];
        if (!r.IsNull()) {
            int idx = 0;
            int numIndicator = 0;
            int sz = opt["contextboxsize"].AsInt();
            wxString fndstr = wxEmptyString;
            if (logFindString.Len() > 30)
                fndstr = logFindString.Left(30) + "...";
            else
                fndstr = logFindString;
            int fndcount = items_find.size();
            if (fndcount > 0) fndstr = wxString::Format("[%d] \"%s\"", fndcount,fndstr);
            wxMenu menu(fndstr);

            int spacewidth,tmp;
            DoGetTextExtent(" ", &spacewidth, &tmp);
            int maxwidth = 0;
            int x, y;
            for (int j = 0; j < r.Size(); j++) {
                if (mark_color[j].count > 0) {
                    wxString counttext = wxString::Format("[%ld] ", mark_color[j].count);
                    DoGetTextExtent(counttext, &x, &y);
                    if (x > maxwidth) maxwidth = x;
                }
            }
            for (int j = 0; j < r.Size(); j++) {
                wxJSONValue indicator = r[j];
                bool enable = indicator["enable"].AsBool();
                wxString name = indicator["menulabel"].AsString();
                if (name=="null") {
                    name = wxString::Format("Marker %d",j);
                }
                // align
                wxString counttext="";
                if (mark_color[j].count > 0) {
                    counttext = wxString::Format("[%ld] ", mark_color[j].count);
                    DoGetTextExtent(counttext, &x, &y);
                    int cnt = (maxwidth - x) / spacewidth;
                    if (x< maxwidth) counttext=wxString(' ',cnt)+ counttext;
                }
                else {
                    int cnt = (maxwidth) / spacewidth;
                    wxString s(' ', cnt);
                    counttext=s+counttext;
                }
                name = wxString::Format("%s%s", counttext, name);
                wxColour clr=GetColorByIndex(j);
                wxMenuItem* mi = new wxMenuItem(&menu, MNU_MARK + j, name,wxEmptyString , wxITEM_CHECK);
                menu.Append(mi);
                mi->Check(enable);
#if defined(__WXMSW__)
                mi->SetBitmaps(CreateBitmap(clr, true,sz), CreateBitmap(clr, false,sz));
#endif
            }
            PopupMenu(&menu, evt.GetPosition());
        }
     //   mi->SetBackgroundColour(wxColour(255, 0, 0));
        
       
    }
}

int ctlNavigatePanel::TryMarkItem(long row, const wxString& str) {
    int sz = search_rule.size();
    int numIndicator = -1;
    bool fnd = false;
    int j = 0;
    while (j < sz) {
        find_s ff= search_rule[j];
        if (ff.i == -1) { j++; continue; } // disable rule
        if (!fnd && ff.e == ff.i) {
            // goto next indicator ecxlude ignore
            j = ff.i;
            continue;
        }
        if (fnd && numIndicator < ff.num) { //  exclude ending
            break;
        }
        bool iswhere = false;
        if (regExArray[j] == NULL)
            iswhere = str.Find(ff.s) >= 0;
        else
            iswhere = regExArray[j]->Matches(str);

        if (iswhere) {
            if (ff.e == ff.i) { // exclude found, continue 
                j = ff.i; // goto next indicator string
                fnd = false;
                numIndicator = -1;
            }
            else {
                // found include string
                // comapre exclude if needed
                numIndicator = ff.num;
                if (ff.e == -1) break; // skip empty exclude
                j = ff.e;
                fnd = true;

            }
        }
        else j++;
    }
    if (numIndicator >= 0) {
        AddMarkItem(row, numIndicator);
        mark_color[numIndicator].count++;
    }
    // find mark
    if (logFindString.length()>0 && (str.Find(logFindString) > -1)) {
        items_find.push_back(row);
    }
    // starting db
    if ((str.Find("\"postmaster\"") > -1)
        &&
        (((str.Find("LOG,00000,\"starting ") > -1 )
        ||
        (str.Find("LOG,00000,\"all server processes terminated; reinitializing") > -1) // postmaster restart iher process

        ))
        && sinterval==-1) {
        // start db
        sinterval = row;
        startdbintervals.push_back(sinterval);
        einterval = -1;
    }
    else if (str.Find("LOG,00000,\"database system is ready to a") > -1) {
        // accept connect db
        einterval = row;
        if (sinterval==-1) startdbintervals.push_back(0);
        startdbintervals.push_back(row);
        sinterval = -1;
    }

    return numIndicator;
}
wxColour ctlNavigatePanel::GetColorByIndex(int colorindex) {
    
    if (colorindex >= 0 && colorindex < mark_color.size()) {
        return mark_color[colorindex].color;
    }
    return *wxBLACK;
}
int ctlNavigatePanel::FindText(wxString findtext, int position, bool directionUp) {
    long lcount = ctrl->GetItemCount();
    long item = 0;
    if (position == HOME && lcount>0) {
        ctrl->Focus(0);
    }
    long itemFocus = ctrl->GetFocusedItem();
    if (items_find.size() == 0) {
        while (item < lcount && item >= 0)
        {
            // this item is selected - do whatever is needed with it
            //wxLogMessage("Item %ld is focused.", item);
            //long fpos = logList->FindItem(item, logFindString, true);
            wxString s = ctrl->GetTextLong(item);
            item++;
            if (!(s.Find(logFindString) > -1)) {
                continue;
            }
            //ctrl->Focus(item);
            //lastPositionFind = item + dir;
            items_find.push_back(item - 1);
        }
        if (items_find.size() == 0) {
            wxBell();
            return 0;
        }
    }
    long nextitem = -1;
    long dir = 1;
    if (directionUp) dir = -1;
    int posF = binary_search(itemFocus, items_find);
    if (items_find[posF] > itemFocus && !directionUp) nextitem = posF;
    if (items_find[posF] < itemFocus && directionUp)  nextitem = posF;
    if (items_find[posF] == itemFocus)  nextitem = posF + dir;
    if (nextitem == -1) {
        if (items_find[posF] > itemFocus && directionUp) nextitem = posF - 1;
        if (items_find[posF] < itemFocus && !directionUp)  nextitem = posF + 1;
    }

    if (nextitem == -1 || nextitem>= items_find.size())
        wxBell();
    else
    {
        ctrl->Focus(items_find[nextitem]);
        RowVisibleCenter(items_find[nextitem]);
    }
    return 0;
}

