
// 
#include "pgAdmin3.h"

#include "frm/frmMain.h"
#include "ctl/ctlGitPanel.h"
#include "ctl/ctlSQLBox.h"
#include "wx/notebook.h"
#include "wx/artprov.h"
#include "wx/creddlg.h"
#include "wx/webrequest.h"
#include "wx/filedlg.h"
#include "wx/image.h"
#include "schema/pgObject.h"
#include "schema/pgServer.h"
#include "utils/json/jsonval.h"
#include "utils/json/jsonwriter.h"
#include "utils/json/jsonreader.h"
#include <wx/stdpaths.h>
#include <wx/wfstream.h>
#include <wx/imaglist.h>
#include "images/gqbAdd.pngc"
#include "images/gqbRemove.pngc"
#include "images/conversion.pngc"
#include "ctl/SourceViewDialog.h"
#include <wx/zstream.h>
#include <wx/mstream.h>
#include <wx/sstream.h>
#include <wx/tarstrm.h>
enum
{
    MARGIN_LINE_NUMBERS
};



#if !defined(NO_WXJSON_GIT)
#if wxUSE_WEBREQUEST
ctlGitPanel::ctlGitPanel(wxWindow* parent, frmMain* form, wxJSONValue cf) :
    wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
{
    formMain = form;
    cfg = cf;

    //, wxPoint(0, 0), wxDefaultSize, wxSUNKEN_BORDER | wxCLIP_CHILDREN

//        Bind(wxEVT_CLOSE_WINDOW, &WebRequestFrame::OnClose, this);

        // Prepare UI controls
    wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);

    mainSizer->Add(new wxStaticText(this, wxID_ANY, "Request URL:"),
        wxSizerFlags().Border());
    m_urlTextCtrl = new wxTextCtrl(this, wxID_ANY,
        "https://www.wxwidgets.org/downloads/logos/blocks.png",
        wxDefaultPosition, wxDefaultSize,
        wxTE_PROCESS_ENTER);
    mainSizer->Add(m_urlTextCtrl,
        wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT));
    //m_urlTextCtrl->Bind(wxEVT_TEXT_ENTER, &ctlGitPanel::OnLoadGitButton, this);

    m_notebook = new wxNotebook(this, wxID_ANY);
    m_notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &ctlGitPanel::OnNotebookPageChanged, this);

    // Image page
    //wxPanel* imagePanel = new wxPanel(m_notebook);
    //wxSizer* imageSizer = new wxBoxSizer(wxVERTICAL);

    //m_imageStaticBitmap = new wxStaticBitmap(imagePanel,
    //    wxID_ANY, wxArtProvider::GetBitmap(wxART_MISSING_IMAGE));
    //imageSizer->Add(m_imageStaticBitmap, wxSizerFlags(1).Expand());

    //imagePanel->SetSizer(imageSizer);
    //m_notebook->AddPage(imagePanel, "Image", true);

    // Text page
    wxPanel* textPanel = new wxPanel(m_notebook);
    wxSizer* textSizer = new wxBoxSizer(wxVERTICAL);

    m_postCheckBox = new wxCheckBox(textPanel, wxID_ANY, "Post request body");
    textSizer->Add(m_postCheckBox, wxSizerFlags().Border());
    m_postCheckBox->Bind(wxEVT_CHECKBOX, &ctlGitPanel::OnPostCheckBox, this);

    m_postRequestTextCtrl = new wxTextCtrl(textPanel, wxID_ANY,
        "app=WebRequestSample&version=1",
        wxDefaultPosition, wxSize(-1, FromDIP(60)), wxTE_MULTILINE);
    textSizer->Add(m_postRequestTextCtrl,
        wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT));

    textSizer->Add(new wxStaticText(textPanel, wxID_ANY, "Request body content type:"),
        wxSizerFlags().Border());
    m_postContentTypeTextCtrl = new wxTextCtrl(textPanel, wxID_ANY,
        "application/x-www-form-urlencoded");
    textSizer->Add(m_postContentTypeTextCtrl,
        wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT));

    textSizer->Add(new wxStaticText(textPanel, wxID_ANY, "Response body:"),
        wxSizerFlags().Border());
    m_textResponseTextCtrl = new wxTextCtrl(textPanel, wxID_ANY, "",
        wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
    m_textResponseTextCtrl->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
    textSizer->Add(m_textResponseTextCtrl,
        wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT | wxBOTTOM));

    textPanel->SetSizer(textSizer);
    m_notebook->AddPage(textPanel, "Debug");

    // Download page
    //wxPanel* downloadPanel = new wxPanel(m_notebook);
    //wxSizer* downloadSizer = new wxBoxSizer(wxVERTICAL);
    //wxStaticText* downloadHeader = new wxStaticText(downloadPanel, wxID_ANY,
    //    "The URL will be downloaded to a file.\n"
    //    "Progress will be shown and you will be asked, where\n"
    //    "to save the file when the download completed.");
    //downloadSizer->Add(downloadHeader, wxSizerFlags().Expand().Border());
    //downloadSizer->AddStretchSpacer();
    //m_downloadGauge = new wxGauge(downloadPanel, wxID_ANY, 100);
    //downloadSizer->Add(m_downloadGauge, wxSizerFlags().Expand().Border());
    //m_downloadStaticText = new wxStaticText(downloadPanel, wxID_ANY, "-1-");
    //downloadSizer->Add(m_downloadStaticText, wxSizerFlags().Expand().Border());

    //downloadSizer->AddStretchSpacer();

    //downloadPanel->SetSizer(downloadSizer);
    //m_notebook->AddPage(downloadPanel, "Download");

    // Advanced page
    //wxPanel* advancedPanel = new wxPanel(m_notebook);
    //wxSizer* advSizer = new wxBoxSizer(wxVERTICAL);
    //wxStaticText* advHeader = new wxStaticText(advancedPanel, wxID_ANY,
    //    "As an example of processing data while\n"
    //    "it's being received from the server, every\n"
    //    "zero byte in the response will be counted below.");
    //advSizer->Add(advHeader, wxSizerFlags().Expand().Border());

    //advSizer->AddStretchSpacer();
    //m_advCountStaticText = new wxStaticText(advancedPanel, wxID_ANY, "0",
    //    wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE);
    //m_advCountStaticText->SetFont(m_advCountStaticText->GetFont()
    //    .MakeBold().MakeLarger().MakeLarger());
    //advSizer->Add(m_advCountStaticText, wxSizerFlags().Expand().Border());
    //advSizer->AddStretchSpacer();

    //advancedPanel->SetSizer(advSizer);

    //m_notebook->AddPage(advancedPanel, "Advanced");
// branch
    wxPanel* branchPanel = new wxPanel(m_notebook);
    wxSizer* brnSizer = new wxBoxSizer(wxVERTICAL);
    brnSizer->Add(new wxStaticText(branchPanel, wxID_ANY, "List branch:"),
        wxSizerFlags().Border());
//    brnSizer->Add(brnHeader, wxSizerFlags().Expand().Border());

    //brnSizer->AddStretchSpacer();
    m_Branch_List_Ctrl = new wxComboBox(branchPanel, wxID_ANY,
        "");
    brnSizer->Add(m_Branch_List_Ctrl, wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT));

    m_branchListButton = new wxButton(branchPanel, wxID_ANY, "Branch List");
    m_branchListButton->Bind(wxEVT_BUTTON, &ctlGitPanel::OnBranchListButton, this);
    //m_cancelButton->Disable();
    brnSizer->Add(m_branchListButton, wxSizerFlags().Border());

    m_branchDeleteButton = new wxButton(branchPanel, wxID_ANY, "Branch Delete");
    m_branchDeleteButton->Bind(wxEVT_BUTTON, &ctlGitPanel::OnBranchDeleteButton, this);
    brnSizer->Add(m_branchDeleteButton, wxSizerFlags().Border());
    branchPanel->SetSizer(brnSizer);
    m_notebook->AddPage(branchPanel, "Branch");

// commits
    wxPanel* commitPanel = new wxPanel(m_notebook);
    wxSizer* cmtSizer = new wxBoxSizer(wxVERTICAL);
    cmtSizer->Add(new wxStaticText(commitPanel, wxID_ANY, "List commit files:"),
        wxSizerFlags().Border());
    //    brnSizer->Add(brnHeader, wxSizerFlags().Expand().Border());

        //brnSizer->AddStretchSpacer();
    
    m_commit_List_View = new wxListView(commitPanel, wxID_ANY, wxDefaultPosition, wxSize(-1,120), wxLC_LIST| wxSIMPLE_BORDER| wxLC_NO_HEADER);
    wxImageList *imaget = new wxImageList(19, 16,false,3);
    imaget->Add(*gqbAdd_png_img);
    imaget->Add(*gqbRemove_png_img);
    //imaget->Add(*conversion_png_img);
    wxBitmap image1 = wxBitmap(conversion_png_img->Scale(19, 16));
    imaget->Add(image1);
    m_commit_List_View->SetImageList(imaget, wxIMAGE_LIST_SMALL);
    m_commit_List_View->Bind(wxEVT_KEY_UP, &ctlGitPanel::OnKEY_UP, this);
    m_commit_List_View->Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, &ctlGitPanel::OnListRClick, this);
    
    cmtSizer->Add(m_commit_List_View, wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT));

    cmtSizer->Add(new wxStaticText(commitPanel, wxID_ANY, "Message commit:"),
        wxSizerFlags().Border());
    m_CommentTextCtrl = new wxTextCtrl(commitPanel, wxID_ANY, "",
        wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
    //m_CommentTextCtrl->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
    cmtSizer->Add(m_CommentTextCtrl,
        wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT | wxBOTTOM));

    m_commitButton = new wxButton(commitPanel, wxID_ANY, "commit");
    m_commitButton->Bind(wxEVT_BUTTON, &ctlGitPanel::OnCommitButton, this);
    //m_cancelButton->Disable();
    cmtSizer->Add(m_commitButton, wxSizerFlags().Border());

//    m_commitDeleteButton = new wxButton(commitPanel, wxID_ANY, "commit Delete");
//   m_commitDeleteButton->Bind(wxEVT_BUTTON, &ctlGitPanel::OncommitDeleteButton, this);
 //   cmtSizer->Add(m_commitDeleteButton, wxSizerFlags().Border());


    commitPanel->SetSizer(cmtSizer);
    m_notebook->AddPage(commitPanel, "Commit");

///////////////////////////

    mainSizer->Add(m_notebook, wxSizerFlags(1).Expand().Border());

    wxStdDialogButtonSizer* btn2Sizer = new wxStdDialogButtonSizer();
    m_cancelButton = new wxButton(this, wxID_CANCEL, "Cancel");
    m_cancelButton->Bind(wxEVT_BUTTON, &ctlGitPanel::OnCancelButton, this);
    m_cancelButton->Disable();
    btn2Sizer->AddButton(m_cancelButton);

    m_startButton = new wxButton(this, wxID_OK, "&Load Git");
    m_startButton->Bind(wxEVT_BUTTON, &ctlGitPanel::OnLoadGitButton, this);
    btn2Sizer->AddButton(m_startButton);
    m_link= new wxStaticText(this, wxID_ANY, "",wxDefaultPosition,wxSize(-1,-1));
   // btn2Sizer->AddStretchSpacer();
    btn2Sizer->Add(m_link, wxSizerFlags(1).Expand().Border(wxLEFT ).Top());
    

    btn2Sizer->Realize();
    mainSizer->Add(btn2Sizer, wxSizerFlags().Expand().Border());

    wxCommandEvent evt;
    OnPostCheckBox(evt);

    //SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
    SetSizer(mainSizer);

    SetSize(FromDIP(wxSize(540, 500)));

    wxLogStatus(this, "%s", wxWebSession::GetDefault().GetLibraryVersionInfo().ToString());

    m_downloadProgressTimer.Bind(wxEVT_TIMER,
        &ctlGitPanel::OnProgressTimer, this);

    GetBranchList(true);
    if (!error_msg.IsEmpty()) {
        // error connect
        wxLogError("GitLab connect error.\n%s", error_msg);
        return;
    }
// load pgadmin3.json
    wxString jsonText = GetRepositoryFile("main", "pgadmin3.json");
    if (!jsonText.IsEmpty()) {
        wxJSONReader reader;
        wxJSONValue c;
        int errnum = reader.Parse(jsonText, &c);
        if (errnum > 0) {
            wxLogError("Branch 'main'. Parse json file pgadmin3.json errors. Number errors %d", errnum);
        }
        else
        {
            if ((!c["ignore_schema"].IsNull()) && cfg["ignore_schema"].IsNull()) cfg["ignore_schema"] = c["ignore_schema"];
            if ((!c["control_objects"].IsNull()) && cfg["control_objects"].IsNull()) cfg["control_objects"] = c["control_objects"];
            if ((!c["maps_branch_to_dbname"].IsNull()) && cfg["maps_branch_to_dbname"].IsNull()) cfg["maps_branch_to_dbname"] = c["maps_branch_to_dbname"];
        }

    }

}
void ctlGitPanel::OnListRClick(wxListEvent& evt) {
    int i = evt.GetIndex();
    wxListItem it=evt.GetItem();
    wxString pat = it.GetText();
    if (it) {
    }
    wxString sql_git = m_git_content[pat];
    wxString sql_db = m_base_content[pat];
    SourceViewDialog* dlg = new SourceViewDialog(NULL, sql_git,sql_db, L"Compare git to DB");
    dlg->Show();

}
void ctlGitPanel::OnKEY_UP(wxKeyEvent& event) {
    bool ctrl = false;
    if ((event.GetModifiers() & wxMOD_CONTROL) == wxMOD_CONTROL) {
        ctrl = true;
    }
    if (event.GetKeyCode() == WXK_DELETE && ctrl) {
        wxArrayInt num;
        for (int i = 0; i < m_commit_List_View->GetItemCount(); i++) {
            if (!m_commit_List_View->IsSelected(i)) continue;
            num.Add(i);
        }
        for (int j = num.Count() - 1; j >= 0; j--) {
            m_commit_List_View->DeleteItem(num[j]);
        }
        event.Skip(true);
    }
    if (event.GetKeyCode() == 'A' && ctrl) {
        for (int i = 0; i < m_commit_List_View->GetItemCount(); i++) {
            m_commit_List_View->Select(i);
        }
        event.Skip(true);
    }
    

}
ctlGitPanel::~ctlGitPanel()
{
    // We have to block until the web request completes, but we need to
    // process events while doing it.
    //Hide();
    if (m_currentRequest.IsOk())
        m_currentRequest.Cancel();

    while (m_currentRequest.IsOk())
    {
        wxYield();
    }
}
wxJSONValue ctlGitPanel::GetConfig() {
    wxString c;
    wxJSONValue cfg;
    extern wxString dataDir;
    wxString p = dataDir+ wxFileName::GetPathSeparator() +"gitlab.json";
    if (!wxFileExists(p)) return cfg;
    wxFileInputStream input(p);
    if (input.IsOk()) {
        wxJSONReader reader;
        int errnum=reader.Parse(input, &cfg);
        if (errnum > 0) {
            wxLogError("Parse json file %s errors. Number errors %d",p,errnum);
        }
        else {
            
        }

    }
    return cfg;
}
wxMemoryBuffer ctlGitPanel::execRequestBinary(wxString url) {
    wxWebRequest request = wxWebSession::GetDefault().CreateRequest(
        this,
        url
    );
    wxMemoryBuffer buf;
    if (!request.IsOk()) {
        // This is not expected, but handle the error somehow.
        return buf;
    }
    wxString pt;
    cfg["private_token"].AsString(pt);
    request.SetHeader("PRIVATE-TOKEN", pt);
    wxString rez;
    wxMemoryOutputStream output;
    
    Bind(wxEVT_WEBREQUEST_STATE, [&rez,&buf](wxWebRequestEvent& evt) {
        switch (evt.GetState())
        {
            // Request completed
        case wxWebRequest::State_Completed:
        {
            wxWebResponse response = evt.GetRequest().GetResponse();
            if (response.GetStatus() == 200) {
                 wxInputStream* stream = response.GetStream();
                 if (stream) {

                // Создаем буфер (например, 4 КБ)
                    char buffer[4096];
                    while (!stream->Eof()) {
                        // Читаем порцию данных из входного потока
                        stream->Read(buffer, sizeof(buffer));
                        
                        // Определяем, сколько байт было реально прочитано
                        size_t bytesRead = stream->LastRead();

                        if (bytesRead > 0) {
                            // Записываем прочитанные байты в файл
                            buf.AppendData(buffer, bytesRead);
                            //output.Write(buffer, bytesRead);
                        }
                    }
                    //output.Close();
                 }
            }

            //buffer = response.GetStream();

            rez=wxString::Format("Loaded %lld bytes binary data (Status: %d %s)",
                evt.GetResponse().GetContentLength(),
                evt.GetResponse().GetStatus(),
                evt.GetResponse().GetStatusText());
            break;
        }
        // Request failed
        case wxWebRequest::State_Unauthorized:
        {
            rez.Printf("%s", evt.GetErrorDescription());
            break;
        }
        case wxWebRequest::State_Failed:
            rez.Printf("%s", evt.GetErrorDescription());
            break;
        }
        });
    error_msg = "";
    request.Start();
    while (request.IsOk() && rez.Len() == 0)
    {
        wxYield();
    }
    m_textResponseTextCtrl->SetLabelText(rez);
    if (!rez.StartsWith("Load")) error_msg=rez;
    return buf;
}

wxJSONValue ctlGitPanel::execRequest(wxString url, wxJSONValue args,wxString cmd) {
    wxWebRequest request = wxWebSession::GetDefault().CreateRequest(
        this,
        url
    );
    wxJSONValue r;
    if (!request.IsOk()) {
        // This is not expected, but handle the error somehow.
        return r;
    }
    wxString pt;
    cfg["private_token"].AsString(pt);
    request.SetHeader("PRIVATE-TOKEN", pt);
    if (cmd != "GET") request.SetMethod(cmd);
    if (!args.IsNull()) {
        wxJSONWriter writer(wxJSONWRITER_NONE);
        wxString     str;
        writer.Write(args, str);
//        str = "{}";
        request.SetData(str, "application/json");
        m_postContentTypeTextCtrl->SetValue(str);
    }
    wxString rez;
    wxString answer;
    wxString link;
    response_link = "";
    // Bind state event
    Bind(wxEVT_WEBREQUEST_STATE, [&link, &rez, &answer](wxWebRequestEvent& evt) {
        switch (evt.GetState())
        {
            // Request completed
        case wxWebRequest::State_Completed:
        {
            answer = evt.GetResponse().AsString();
            rez.Printf("Loaded %lld bytes text data (Status: %d %s)",
                evt.GetResponse().GetContentLength(),
                evt.GetResponse().GetStatus(),
                evt.GetResponse().GetStatusText());
            link = evt.GetResponse().GetHeader("Link");
            break;
        }
        // Request failed
        case wxWebRequest::State_Unauthorized:
        {
            rez.Printf("%s", evt.GetErrorDescription());
            break;
        }
        case wxWebRequest::State_Failed:
            rez.Printf("%s", evt.GetErrorDescription());
            break;
        }
        });
    error_msg = "";
    request.Start();
    while (request.IsOk() && rez.Len() == 0)
    {
        wxYield();
    }
    m_textResponseTextCtrl->SetLabelText(rez);
    if (rez.Len() > 0 && !rez.StartsWith("Loaded")) {
        //m_textResponseTextCtrl->SetValue(answer);
        error_msg = rez;
    }
    wxJSONReader reader;
    wxJSONValue root;
    // now read the JSON text and store it in the 'root' structure
    // check for errors before retreiving values...
    if (!answer.IsEmpty()) {
        response_link = link;

        int numErrors = reader.Parse(answer, &root);
        if (numErrors > 0) {
           // m_downloadStaticText->SetLabelText("Error parser JSON text response");
        }
    }
    return root;
}
void ctlGitPanel::CommandBranch(wxString branchName,wxString cmd) {
    wxJSONValue r;
    wxString ur;
    cfg["url"].AsString(ur);
    wxString pr;
    cfg["project_id"].AsString(pr);
    ur = ur + "projects/" + pr + "/repository/branches";
    wxJSONValue rez;
    r["branch"] = branchName;
    if (cmd == "create") {
        r["ref"] = wxString("main");
        rez = execRequest(ur, r, "POST");
    }
    else {
        rez = execRequest(ur, r, "DELETE");

    }

}
void ctlGitPanel::GetBranchList(bool refresh) {
    wxJSONValue r;
    wxString ur;
    cfg["url"].AsString(ur);
    wxString pr;
    cfg["project_id"].AsString(pr);
    ur = ur+"projects/"+pr+"/repository/branches";
    
    if (m_Branch_List_Ctrl->GetCount()==0 || refresh) {
        m_Branch_List_Ctrl->Clear();
        wxJSONValue rez = execRequest(ur, r,"GET");
        for (int i = 0; i < rez.Size(); i++) {
            wxJSONValue b = rez[i];
            wxString name;
            b["name"].AsString(name);
            m_Branch_List_Ctrl->Append(name);
        }
    }
    int i;
    for (i = 0; i < m_Branch_List_Ctrl->GetCount(); i++) {
        if (m_Branch_List_Ctrl->GetString(i) == currentDBname) {
            m_Branch_List_Ctrl->SetSelection(i);
            break;
        }
    }
    if (currentDBname.IsEmpty()) return;

    if (i >= m_Branch_List_Ctrl->GetCount()) {
        CommandBranch(currentDBname, "create");
        m_Branch_List_Ctrl->Append(currentDBname);
        m_Branch_List_Ctrl->SetSelection(i);
    }
    

}
bool ctlGitPanel::CheckValidObject(pgObject *o) {
    if (o ) {
        if (o->GetMetaType() == PGM_VIEW
            || o->GetMetaType() == PGM_FOREIGNTABLE
            || o->GetMetaType() == PGM_FUNCTION
            || o->GetMetaType() == PGM_TABLE
            || o->GetMetaType() == PGM_TRIGGER
            || o->GetMetaType() == PGM_SCHEMA
            )
            return true;
    }
    if (o && !o->IsCollection()) {

    }
    return false;
}
void ctlGitPanel::ShowPage(pgObject* data) {
    m_textResponseTextCtrl->Clear();
   // wxString path(formMain->GetNodePath(data->GetId()));

    wxString path;
    wxString msg = "not support";
    ctlTree* browser= formMain->GetBrowser();
    wxTreeItemId node= data->GetId();
    path = browser->GetItemText(node).BeforeFirst('(').Trim();
    if (data->GetTypeName() == "Procedure" 
        || data->GetTypeName() == "Function"
        || data->GetTypeName() == "Trigger Function"
        || data->GetTypeName() == "Event Trigger"
        )
        path = browser->GetItemText(node).Trim();
    wxJSONValue r;
    wxString typenam= data->GetTypeName();
    bool ex = false;
    bool valid = true;
    r = cfg["control_objects"];
    if (!data->IsCollection()) {
        typenam = data->GetFactory()->GetCollectionFactory()->GetTypeName();
    } 
    if (!r.IsNull()) {
        bool nx = true;
        for (int j = 0; j < r.Size(); j++) {
            wxString ss = r[j].AsString();
            if (ss == typenam) { nx=false; break; };
        }
        if (nx) { valid = false; ex = true; msg = wxString::Format("'%s'not \"control_objects\"", typenam); };
    }
    wxTreeItemId parent = browser->GetItemParent(node);
    
    
    while (parent.IsOk())
    {
        pgObject* o = browser->GetObject(parent);
        if (o) typenam = o->GetTypeName();

        if (o && (typenam == "Schemas"
            || typenam == "Schema"
            )) {
            //valid = CheckValidObject(data);
            if (o && typenam == "Schema") {
                r = cfg["ignore_schema"];
                if (!r.IsNull()) {
                    for (int j = 0; j < r.Size(); j++) {
                        wxString ss = r[j].AsString();
                        if (ss == o->GetName()) {
                            valid = false;
                            msg = "schema ignore";
                            ex = true;
                        }
                    }
                    
                }
            }
        }
        if (typenam == "Database") {
            ex = true;
            currentDBname = o->GetName();
            nodeDB = o->GetId();
        }
        if (o && o->IsCollection()) {
            r = cfg["control_objects"];
            if (!r.IsNull()) {
                bool nx = true;
                for (int j = 0; j < r.Size(); j++) {
                    wxString ss = r[j].AsString();
                    if (ss == typenam) { nx = false; break; };
                }
                if (nx) { valid = false; ex = true; msg = wxString::Format("'%s'not \"control_objects\"", typenam); };
            }

        }
        path = browser->GetItemText(parent).BeforeFirst('(').Trim() + wxT("/") + path;
        o = browser->GetObject(parent);
        if (o && o->GetMetaType() == PGM_TABLE && !o->IsCollection()) {
            // without partitions
            path = browser->GetItemText(parent).BeforeFirst('(').Trim();
            node = parent;
            data = o;
        }
        parent = browser->GetItemParent(parent);
        if (ex) break;
    }
    if (syncDBname != currentDBname) {
        valid = false;
        msg = wxString::Format("diiference db name and load git db '%s'<>'%s'", currentDBname, syncDBname);
    }
    if (!valid) {
        m_link->SetLabel(msg);
        return;
    }
    wxString stype = data->GetTypeName();
    m_link->SetLabel(path+" - "+ stype);
    if (stype=="Schema") return;
    if (data->IsCollection()) { 

        return;
    }
    // need load gitlad
    if (syncDBname!=currentDBname || currentDBname.IsEmpty()) return;

    wxString sq;
    
    wxStringToStringHashMap::iterator it;
        it = m_treeName.find(path);
        if (it == m_treeName.end()) {
            m_treeName[path] = "new";
            it = m_treeName.find(path);
        }
            wxString v = it->second;
            sq = data->GetSql(browser);
            sq.Replace("\r\n", "\n");
            if (v == "new") {
                // add commit
                ReplaceItem(path, Files::Add_File);
                m_base_content[path] = sq;

            }
            else {
                wxString s2 = m_git_content[path];
                if (s2 != sq) {
                    m_treeName[path] = "update";
                    m_base_content[path] = sq;
                    ReplaceItem(path, Files::Update_File);
                }
                else {
                    m_treeName[path] = "equal";
                    ReplaceItem(path, -1);
                }

            }
}
int ctlGitPanel::ReplaceItem(wxString path,int image) {
    int i = m_commit_List_View->FindItem(-1, path);
    if (i != wxNOT_FOUND) m_commit_List_View->DeleteItem(i);
        else          i = m_commit_List_View->GetItemCount();
    if (image == -1) return i;
    return m_commit_List_View->InsertItem(i, path, image);
}
wxString url_encode(const wxString param) {
    
    wxCharBuffer  utf8CB = param.utf8_str();
    char *readBuff = utf8CB.data();
    size_t len = strlen(readBuff);
    wxString encode;
    for (int i = 0; i < len;i++) {
        char c = readBuff[i];
        if ((c>0) && ((isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~'|| c=='='))) {
            encode.Append(c);
            continue;
        }
        encode.Append(wxString::Format("%%%02X", (unsigned char)c));
    }
    
    return encode;

}
wxString base64_decode(const wxString enc) {
    wxString str;
    wxMemoryBuffer buf;
    buf = wxBase64Decode(enc);
    str = wxString::FromUTF8((char*)buf.GetData(), buf.GetDataLen());
    return str;
}
wxString base64_encode(const wxString dec) {
    wxString str;
    wxCharBuffer cb;
    cb=dec.ToUTF8();
    str = wxBase64Encode(cb.data(),cb.length());
    //str = wxString::FromUTF8((char*)buf.GetData(), buf.GetDataLen());
    return str;
}
wxString ctlGitPanel::getCurBranch(wxString dbname) {
    wxJSONValue r = cfg["maps_branch_to_dbname"];
    wxString br;
    if (!r.IsNull()) {
        for (int j = 0; j < r.Size(); j++) {
            wxJSONValue r2 = r[j];
            wxString  b= r2["branch"].AsString();
            wxJSONValue r3 = r2["list_db"];
            for (int k = 0; k < r3.Size(); k++) {
                if (dbname == r3[k].AsString()) {
                    br= b;
                    goto ex_for;
                }
            }
        }

    }
    br = dbname;
ex_for:
    int i=m_Branch_List_Ctrl->FindString(br);
    if (i < 0) {
        wxMessageBox("Not found branch for db name = " + dbname);
        br = "";
    }
    return br;
}
void ctlGitPanel::GetRepositoryArchive(wxString branchName) {
    wxJSONValue r;
    wxString ur;
    //m_git_tree[path] = "need load";

    cfg["url"].AsString(ur);
    wxString pr;
    cfg["project_id"].AsString(pr);
    ur = ur + "projects/" + pr + "/repository/archive";
    wxJSONValue rez;
    wxJSONValue prrr;
    r["sha"] = getCurBranch(branchName);
    ur = ur + ArgsForGet(r);
    m_urlTextCtrl->SetValue(ur);
    wxMemoryBuffer buf=execRequestBinary(ur);
    if (buf.GetDataLen()>0) {
         wxMemoryInputStream zipstream(buf.GetData(), buf.GetDataLen());
         wxZlibInputStream gzIn(zipstream);
         wxTarInputStream tarIn(gzIn);
            wxString entryName;
            wxTarEntry* entry;
            // 4. Читать записи архива по одной
            while (entry = tarIn.GetNextEntry()) {
                entryName = entry->GetName();
                if (!entry->IsDir()) {
                    wxString filecontext;
                    wxStringOutputStream out;
                    tarIn.Read(out);
                    filecontext=out.GetString();
                    wxString path=branchName+'/'+entryName.AfterFirst('/');
                    out.Close();
                        m_git_content[path] = filecontext;
                        m_treeName[path] = "git";
                        m_count_git++;
                }
                delete entry; // Освободить память
            }

    }
}
void ctlGitPanel::GetRepositoryTree(wxString branchName, wxString path, wxString typeElement, wxString value) {
    wxJSONValue r;
    wxString ur;
    //m_git_tree[path] = "need load";

    cfg["url"].AsString(ur);
    wxString pr;
    cfg["project_id"].AsString(pr);
    ur = ur + "projects/" + pr + "/repository/tree";
    wxJSONValue rez;
    wxJSONValue prrr;
    r["ref"] = getCurBranch(branchName);
    wxString rpath = path.AfterFirst('/');
    r["pagination"] = wxString("keyset");
    r["recursive"] = wxString("true");
    r["per_page"] = 90;
    r["path"] = wxString("");
    ur = ur + ArgsForGet(r);
    m_urlTextCtrl->SetValue(ur);
    while (true)
    {
        rez=execRequest(ur, prrr, "GET");
        if (rez.AsArray()) {
            wxString prefix = path.BeforeFirst('/');
            for (int j = 0; j < rez.Size(); j++) {
                wxJSONValue ss = rez[j];
                wxString name = branchName + "/"+ss["path"].AsString();
                if (ss["type"].AsString() != "tree") { 
                    m_treeName[name] = "git";
                    m_count_git++;
                }
                //if (name==path) m_treeName[path] = "both";
            }
            if (!response_link.IsEmpty()) {
                int st = 0;
                wxString tmp;
                while (true) {
                    int p3 = response_link.find(',',st);
                    if (p3 < 0) p3 = response_link.Len();
                    if (p3 > 0) {
                        tmp = response_link.substr(st, p3 - st);
                        int p1 = tmp.Find("rel=\"next");
                        if (p1 < 0 && p3 == response_link.Len()) {
                            return;
                        };
                        if (p1 > 0) {
                            int e = tmp.Index('>');
                            int s = tmp.Index('<');
                            ur = tmp.substr(s + 1, e - s - 1);
                            break;
                        }
                        st = p3 + 1;

                    }
                    else break;
                }
                //ur = response_link;
                continue;
            }
        }
        break;
    }
    return;
}
wxString ctlGitPanel::ArgsForGet(wxJSONValue r) {
    wxArrayString key = r.GetMemberNames();
    wxString params;
    wxJSONValue rez;
    for (int i = 0; i < key.Count(); i++) {
        //if (key[i]=="path") 
            wxString p = key[i] + "=" + r[key[i]].AsString();
            if (params.IsEmpty()) params = "?";
            else params += "&";
            params = params +  url_encode(p);
    }

    return params;
}

void ctlGitPanel::OnCommitButton(wxCommandEvent& WXUNUSED(evt)) {
    wxJSONValue p,act,f;
    wxString br = getCurBranch(currentDBname);
    if (br != currentDBname) {
        wxMessageBox("Branch name not equals DB name. Commit denied.");
        return;
    }
    p["branch"] = br;
    int cnt = 0;
    wxString comment = m_CommentTextCtrl->GetValue();
    if (comment.IsEmpty()) {
        wxMessageBox("Commit name is Empty");
        return;
    }
    
    p["commit_message"] = comment;
    wxArrayInt num;
    for (int i = 0; i < m_commit_List_View->GetItemCount(); i++) {
        if (!m_commit_List_View->IsSelected(i)) continue;
        wxString path = m_commit_List_View->GetItemText(i);
        wxString rpath = path.AfterFirst('/');
        if (m_treeName[path] == "update") f["action"] = wxString("update");
        if (m_treeName[path] == "new") f["action"] = wxString("create");
        if (m_treeName[path] == "git") f["action"] = wxString("delete");
        f["file_path"] = rpath;
        if (f["action"].AsString() != "delete") {
            f["encoding"] = wxString("base64");
            wxString cont = m_base_content[path];
            wxString enc = base64_encode(cont);
            f["content"] = enc;
        }
        act.Append(f);
        num.Add(i);
        cnt++;

    }
    if (cnt > 0) {
        p["actions"] = act;
        bool re=ApplyCommit(currentDBname, p);
        if (re) {
            for (int j = num.Count() - 1; j >= 0; j--) {
                wxString path = m_commit_List_View->GetItemText(num[j]);
                m_commit_List_View->DeleteItem(num[j]);
                if (m_treeName[path] == "update") {
                    m_treeName[path] = "equal";
                    m_git_content[path]= m_base_content[path];
                }
                else if (m_treeName[path] == "new") {
                    m_treeName[path] = "equal";
                    m_git_content[path] = m_base_content[path];
                }
                else if (m_treeName[path] == "git") {
                    wxStringToStringHashMap::iterator it;
                    it=m_treeName.find(path);
                    m_treeName.erase(it);
                }



            }
        }
    }
}
bool ctlGitPanel::ApplyCommit(wxString branchName, wxJSONValue params) {
    wxJSONValue r;
    wxString ur;
    cfg["url"].AsString(ur);
    wxString pr;
    cfg["project_id"].AsString(pr);
    ur = ur + "projects/" + pr + "/repository/commits";
    wxJSONValue rez;
    //    r["pagination"] = wxString("keyset");
    //    r["recursive"] = wxString("true");
    //    r["path"] = wxString("");
    ur = ur + ArgsForGet(r);
    m_urlTextCtrl->SetValue(ur);
    rez = execRequest(ur, params, "POST");
    if (!rez.IsNull()) {
        wxString u = rez["web_url"].AsString();
        m_link->SetLabel(u);
        return true;
    }
    return false;
}
wxString ctlGitPanel::GetRepositoryFile(wxString branchName, wxString path) {
    wxJSONValue r;
    wxString ur;
    //m_git_tree[path] = "need load";

    cfg["url"].AsString(ur);
    wxString pr;
    cfg["project_id"].AsString(pr);
    wxString rpath = path.AfterFirst('/');
    if (rpath.IsEmpty()) rpath = path;
    ur = ur + "projects/" + pr + "/repository/files/"+url_encode(rpath);
    wxJSONValue rez;
    r["ref"] = getCurBranch(branchName);
//    r["pagination"] = wxString("keyset");
//    r["recursive"] = wxString("true");
//    r["path"] = wxString("");
    ur = ur + ArgsForGet(r);
    m_urlTextCtrl->SetValue(ur);
    rez = execRequest(ur, rez, "GET");
    if (!rez.IsNull()) {
        wxString cont = rez["content"].AsString();
        wxString encod= rez["encoding"].AsString();
        wxString str;
        if (encod == "base64") {
            cont = base64_decode(cont);
        }
        m_git_content[path] = cont;
        return cont;
    }
    return "";
}
void ctlGitPanel::GetExpandedChildNodes(wxTreeItemId node, wxArrayString& expandedNodes, wxString pat,  int lvl)
{
    wxTreeItemIdValue cookie;
    wxTreeItemId child;
    ctlTree* browser = formMain->GetBrowser();
    if (lvl == 0) child = node;
    else child = browser->GetFirstChild(node, cookie);
    pgObject* obj;
    wxString path;
    time_t tmp;
    int size = expandedNodes.Count();
    while (child.IsOk())
    {
        obj = browser->GetObject(child);
        wxString typenam;
        wxString s;
        if (obj) {
            typenam = obj->GetTypeName();
            wxJSONValue r;

            if (typenam == "Schema") {
                r = cfg["ignore_schema"];
                if (!r.IsNull()) {
                    for (int j = 0; j < r.Size(); j++) {
                        wxString ss = r[j].AsString();
                        if (ss == obj->GetName())  goto nex;
                    }

                }
            }

            if (obj->GetMetaType() == PGM_CATALOG
                || obj->GetMetaType() == PGM_COLUMN
                || obj->GetMetaType() == PGM_RULE
                || obj->GetMetaType() == PGM_CATALOG
                ) {
                child = browser->GetNextChild(node, cookie);
                continue;
            }
            r = cfg["control_objects"];
            if (!r.IsNull()&& obj->IsCollection()) {
                bool nx = true;
                for (int j = 0; j < r.Size(); j++) {
                    wxString ss = r[j].AsString();
                    if (ss == typenam) { nx = false; break; };
                }
                if (nx) goto nex;
            }

            if ((obj->GetMetaType() == PGM_SCHEMA
                || obj->GetMetaType() == PGM_DATABASE
                || obj->GetMetaType() == PGM_TABLE
                || obj->GetMetaType() == PGM_FOREIGNTABLE
                ) && !obj->IsCollection()) {
                obj->ShowTreeDetail(browser);
                //obj->ShowTree(parent,browser);
            }
            else
            {
                if (obj->GetMetaType() == PGM_VIEW) obj->ShowTreeDetail(browser); // ������ ��� ���� ����� �������� ���� � ���������
                if (obj->GetMetaType() == PGM_EVENTTRIGGER)  // �������� ���� � ��������� �� ��������
                    obj->ShowTreeDetail(browser);
            }

        }
        if (browser->HasChildren(child))
        {
            bool rec = true;
            if (obj && (obj->GetMetaType() == PGM_TABLE
                //||obj->GetMetaType()==PGM_VIEW
                )) {
                wxTreeItemId Item = browser->GetItemParent(child);
                obj = browser->GetObject(Item); // Tables
                wxTreeItemId Item2 = browser->GetItemParent(obj->GetId());
                obj = browser->GetObject(Item2); // Schemes
                if (obj && obj->GetMetaType() == PGM_SCHEMA && !obj->IsCollection()) {
                    rec = false; // �� �������� ���� �� ���. �������� � �������, � �� ������ �� �������
                    obj = browser->GetObject(child);
                    obj->ShowTreeDetail(browser);
                }
                else obj = browser->GetObject(child);
            }
            if (obj && (obj->GetMetaType() == PGM_VIEW && !obj->IsCollection())) rec = false;
            if (rec) {
                GetExpandedChildNodes(child, expandedNodes, pat + '/' + browser->GetItemText(child).BeforeFirst('(').Trim(), lvl + 1);
                //expandedNodes.Add(parent->GetNodePath(child));
                obj = browser->GetObject(child);
            }

        }
        if (obj) {
            
            s = obj->GetSql(browser);
            if ((typenam != "Schema" && typenam != "Database")&& !obj->IsCollection()) {
                //����� SQL ��� ����� �� ���������, ��� ��� ��� ��������� ������� � �� ���� � Git
                wxString p = pat + '/' + browser->GetItemText(child).Trim();
                //m_link->SetLabel(p);
                formMain->SetStatusText(p);
                m_count_db++;
                expandedNodes.Add(p);
                s.Replace("\r\n", "\n");
                m_base_content[p] = s;
                wxStringToStringHashMap::iterator it;
                it = m_treeName.find(p);
                if (it != m_treeName.end()) {
                    wxString v = it->second;
                    wxString g_path= it->first;
                    if (v == "git") {
                        wxString s2;
                        auto it = m_git_content.find(p);
                        if (it != m_git_content.end()) 
                                s2=it->second;
                            else {
                                s2 = GetRepositoryFile(currentDBname, p);
                            }
                        if (!s2.IsSameAs(s)) {
                            m_treeName[p] = "update";
                            ReplaceItem(p, Files::Update_File);
                        }
                        else
                            m_treeName[p] = "equal";
                    }
                }
                else {
                    // not found git
                    m_treeName[p] = "new";
                    ReplaceItem(p, Files::Add_File);

                }

            }
        }
        
nex:
        child = browser->GetNextChild(node, cookie);
    }
}
void ctlGitPanel::OnLoadGitButton(wxCommandEvent& WXUNUSED(evt))
{
    wxTreeItemId node;
    wxArrayString expandedNodes;
    m_startButton->Disable();
    wxString msg = "Load gitlab db name = " + currentDBname;
    syncDBname = "";
    if (nodeDB.IsOk()) {
        m_git_content.clear();
        m_base_content.clear();
        m_treeName.clear();
        m_base_tree.clear();
        m_count_git = 0; m_count_db = 0;
        //m_commit_List_View
        m_commit_List_View->DeleteAllItems();
        if (getCurBranch(currentDBname).IsEmpty()) {
            goto ex;
        };
        
        wxString path = '/';
        if (true)
                GetRepositoryArchive(currentDBname);
            else
                GetRepositoryTree(currentDBname,path,"", "");

        if (!error_msg.IsEmpty()) { msg = error_msg;  goto ex; }
        GetExpandedChildNodes(nodeDB, expandedNodes, currentDBname, 1);
        wxStringToStringHashMap::iterator it= m_treeName.begin();
        // only git
        for (wxStringToStringHashMap::iterator en = m_treeName.end(); it != en; ++it)
        {
            path = it->first;
            if ((it->second) == "git") {
                ReplaceItem(path,Files::Remove_File);
            }
        }

        for (int i = 0; i < expandedNodes.GetCount(); i++) {
            wxString s = expandedNodes[i];
            //m_commit_List_View->InsertItem(m_commit_List_View->GetItemCount(), s, 2);
        }

        msg = wxString::Format("%s (git obj %d; db obj %d)",msg, m_count_git, m_count_db);
        syncDBname = currentDBname;
    }
ex:
    m_link->SetLabel(msg);
    m_startButton->Enable();
}

void ctlGitPanel::OnCancelButton(wxCommandEvent& WXUNUSED(evt))
{
    if (m_currentRequest.IsOk())
        m_currentRequest.Cancel();
}


void ctlGitPanel::OnProgressTimer(wxTimerEvent& WXUNUSED(evt))
{
}

void ctlGitPanel::OnPostCheckBox(wxCommandEvent& WXUNUSED(evt))
{
    m_postContentTypeTextCtrl->Enable(m_postCheckBox->IsChecked());
    m_postRequestTextCtrl->Enable(m_postCheckBox->IsChecked());
    wxColour textBg = wxSystemSettings::GetColour(
        (m_postCheckBox->IsChecked()) ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE);

    m_postContentTypeTextCtrl->SetBackgroundColour(textBg);
    m_postRequestTextCtrl->SetBackgroundColour(textBg);
}


void ctlGitPanel::OnClose(wxCloseEvent& event)
{
    if (m_currentRequest.IsOk())
    {
        if (event.CanVeto())
        {
            wxMessageDialog dialog
            (
                this,
                "A web request is in progress, "
                "closing the window will cancel it.",
                "Please confirm",
                wxYES_NO
            );
            dialog.SetYesNoLabels("Cancel and close", "Don't close");

            if (dialog.ShowModal() != wxID_YES)
            {
                event.Veto();
                return;
            }
        }

        m_currentRequest.Cancel();
    }

    event.Skip();
}

void ctlGitPanel::OnNotebookPageChanged(wxBookCtrlEvent& event)
{
    //SourceViewDialog* dlg = new SourceViewDialog(NULL, "helo\nworld\n", "helo\nworld right\n3 line\n4 line\n");
    //dlg->Show();

}
#endif
#endif