//---------------------------------------------------------------------------

#include <vcl.h>
//#include <iostream>
#include <DBXJSON.hpp>
#include "IdBaseComponent.hpp"
#include "IdComponent.hpp"
#include "IdHTTP.hpp"
#include "IdTCPClient.hpp"
#include "IdTCPConnection.hpp"

//#include "IdIcmpClient.hpp"
//#include "IdRawBase.hpp"
//#include "IdRawClient.hpp"
//#include "IdAuthenticationDigest.hpp"

#pragma hdrstop

//#include "McJSON.hpp"
//https://github.com/hydrobyte/McJSON
//https://github.com/fabriciocolombo/delphi-rest-client-api
#include "RestObjectF.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

TRestData::TRestData()
{
}
//---------------------------------------------------------------------------
TRestData::~TRestData()
{
}
//---------------------------------------------------------------------------

TRestObject::TRestObject()
{
}
//---------------------------------------------------------------------------
TRestObject::~TRestObject()
{
}
//---------------------------------------------------------------------------

TRestObjectList::TRestObjectList()
{
}
//---------------------------------------------------------------------------
TRestObjectList::~TRestObjectList()
{
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
//TRestObjectManager *ObjCtlrManager = NULL;
//---------------------------------------------------------------------------

TRestObjectManager::TRestObjectManager()
{
}
//---------------------------------------------------------------------------
TRestObjectManager::~TRestObjectManager()
{
}
//---------------------------------------------------------------------------

String TRestObjectManager::Get(String AApiUrl, TForm *AParent)
{
//S := TIdURI.URLEncode(str);
    String         result  = "";
    TIdHTTP       *IdHTTP1 = new TIdHTTP(NULL);
    TMemoryStream *stream  = new TMemoryStream();
    TMemo         *memo    = new TMemo(AParent);
    try {
        try {
            memo->Visible = false;
            memo->Parent = AParent;

            IdHTTP1->Request->Clear();
            IdHTTP1->Request->BasicAuthentication = false;
            IdHTTP1->Request->ContentType = "application/json; charset=utf8";
            IdHTTP1->Request->Accept      = "application/json; charset=utf8";
            IdHTTP1->Request->CharSet     = "utf-8";
            //IdHTTP1->Request->ContentEncoding = "utf-8";
            IdHTTP1->ReadTimeout = 20000;
            IdHTTP1->AllowCookies = false;

            IdHTTP1->HTTPOptions = IdHTTP1->HTTPOptions >> hoInProcessAuth;

            //IdHTTP1->IOHandler->DefStringEncoding = TEncoding::GetEncoding(949);
            //result = IdHTTP1->Get(AApiUrl);
            //return result;
            IdHTTP1->Get(AApiUrl, stream);
            stream->Position = 0;
            memo->Lines->BeginUpdate();
            memo->Lines->LoadFromStream(stream, TEncoding::UTF8);
            memo->Lines->EndUpdate();
            result = memo->Text.Trim();

            IdHTTP1->Disconnect();
            IdHTTP1->IOHandler->InputBuffer->Clear();
        } catch(Exception &e) {
        }
    } __finally {
        if (IdHTTP1) delete IdHTTP1;
        if (stream) delete stream;
        if (memo) delete memo;
    }
    return result;

//RawByteString rbstr = (char*)(urlStream->Memory);
//if (Pos("utf-8", IdHTTP1->Response->ContentType)==0 && (Pos("charset=utf-8", rbstr)==0))
//    SetCodePage(rbstr, 949, false);
//else
//SetCodePage(rbstr, 65001, false);
//String sResult = rbstr;
}
//---------------------------------------------------------------------------

TStream* TRestObjectManager::GetStream(String AApiUrl)
{
    String         result  = "";
    TIdHTTP       *IdHTTP1 = new TIdHTTP(NULL);
    TMemoryStream *stream  = new TMemoryStream();
    try {
        try {
            IdHTTP1->Request->Clear();
            IdHTTP1->Request->BasicAuthentication = false;
            IdHTTP1->Request->ContentType = "application/json; charset=utf8";
            IdHTTP1->Request->Accept      = "application/json; charset=utf8";
            IdHTTP1->Request->CharSet     = "utf-8";
            //IdHTTP1->Request->ContentEncoding = "utf-8";
            IdHTTP1->ReadTimeout = 20000;
            IdHTTP1->AllowCookies = false;

            IdHTTP1->HTTPOptions = IdHTTP1->HTTPOptions >> hoInProcessAuth;

            //IdHTTP1->IOHandler->DefStringEncoding = TEncoding::GetEncoding(949);
            //result = IdHTTP1->Get(AApiUrl);
            //return result;
            IdHTTP1->Get(AApiUrl, stream);
            stream->Position = 0;

            IdHTTP1->Disconnect();
            IdHTTP1->IOHandler->InputBuffer->Clear();
        } catch(Exception &e) {
        }
    } __finally {
        if (IdHTTP1) delete IdHTTP1;
    }
    return stream;

//RawByteString rbstr = (char*)(urlStream->Memory);
//if (Pos("utf-8", IdHTTP1->Response->ContentType)==0 && (Pos("charset=utf-8", rbstr)==0))
//    SetCodePage(rbstr, 949, false);
//else
//SetCodePage(rbstr, 65001, false);
//String sResult = rbstr;
}
//---------------------------------------------------------------------------
#if 0
void TRestObjectManager::IdHTTPAuthorization(TObject *Sender, TIdAuthentication *Authentication, bool &Handled)
{
#if 0
    IdHTTP2->Request->Username = FStrmUser;
    IdHTTP2->Request->Password = FStrmPswd;
#endif

    Authentication->Username = "";
    Authentication->Password = "";

    //TIdDigestAuthentication *pAuth1 = (TIdDigestAuthentication*)IdHTTP1->Request->Authentication;
    //TIdDigestAuthentication *pAuth2 = (TIdDigestAuthentication*)Authentication;
    //pAuth1->Uri = IdHTTP1->Request->URL;
    //pAuth2->Method = "GET";

    Handled = true;
}
//---------------------------------------------------------------------------
#endif

bool TRestObjectManager::RequestPost(String AApiUrl, String AJsonData, String &AResult)
{
    bool result = true;
    TIdHTTP       *IdHTTP1 = new TIdHTTP(NULL);
    TStringStream *request = new TStringStream(AJsonData, TEncoding::UTF8, false);
    TMemoryStream *response  = new TMemoryStream();
    AResult = "";

    try {
        try {
            IdHTTP1->Request->Clear();
            //IdHTTP1->Request->BasicAuthentication = false;
            IdHTTP1->Request->ContentType = "application/json; charset=utf8";
            IdHTTP1->Request->Accept      = "application/json; charset=utf8";
            IdHTTP1->Request->CharSet     = "utf-8";
            //IdHTTP1->Request->ContentEncoding = "utf-8";
            IdHTTP1->ConnectTimeout = 5000;
            IdHTTP1->ReadTimeout = 20000;
            IdHTTP1->AllowCookies = false;
            //IdHTTP1->Request->CustomHeaders->Add('Authorization: Bearer ' + sToken);

            IdHTTP1->HTTPOptions = IdHTTP1->HTTPOptions >> hoInProcessAuth;
            IdHTTP1->HTTPOptions = IdHTTP1->HTTPOptions << hoForceEncodeParams;

            request->Position = 0;
            IdHTTP1->Response->KeepAlive = false;
            IdHTTP1->Response->ContentType     = "application/json; charset=utf8";
            IdHTTP1->Response->CharSet         = "utf-8";
            //IdHTTP1->Response->ContentEncoding = "utf-8";
            IdHTTP1->Post(AApiUrl, request, response);
            IdHTTP1->Disconnect();
            IdHTTP1->IOHandler->InputBuffer->Clear();

            //TMcJsonItem *Json = new TMcJsonItem();
            //Json->LoadFromStream(response, true);
        } catch(EIdHTTPProtocolException &e1) {
            result = false;

#if 0
            String sErrMsg = e1.ErrorMessage;
            TMcJsonItem *errJson = NULL;
            try {
                try {
                    TMcJsonItem *errJson = new TMcJsonItem();
                    errJson->AsJSON = e1.ErrorMessage;
                } catch(Exception &e) {
                }
            } __finally {
                if (errJson) delete errJson;
            }

            Application->MessageBox(e1.ErrorMessage.c_str(), L"작업 중 오류 발생.",  MB_OK|MB_ICONERROR|MB_APPLMODAL);
            TStringStream *errStream = new TStringStream(e1.ErrorMessage, TEncoding::UTF8, false);

            RawByteString rbstr = (char*)(AnsiString(e1.ErrorMessage).c_str());
            SetCodePage(rbstr, 949, false);
            String sResult = rbstr;
            Application->MessageBox(sResult.c_str(), L"작업 중 오류 발생.",  MB_OK|MB_ICONERROR|MB_APPLMODAL);
#endif

            String errMsg;
            switch(e1.ErrorCode) {
            case 400:
                errMsg = "요청 데이터가 유효하지 않습니다.";
                break;
            case 404:
                errMsg = "데이터를 찾을 수 없습니다.";
                break;
            case 417:
                errMsg = "작업 중 오류가 발생 하였습니다.";
                break;
            default:
                errMsg = "작업 중 알 수 없는 오류가 발생 하였습니다.";
                break;
            }
            errMsg = e1.Message + "\r\n" + errMsg;
            Application->MessageBox(errMsg.c_str(), L"작업 중 오류 발생.",  MB_OK|MB_ICONERROR|MB_APPLMODAL);
        } catch(Exception &e) {
            result = false;
            Application->ShowException(&e);
        }
    } __finally {
        if (IdHTTP1) delete IdHTTP1;
        if (request) delete request;
        if (response) delete response;
    }

    return result;
}
//---------------------------------------------------------------------------

TMcJsonItem* TRestObjectManager::RequestGet(String AApiUrl)
{
    TMcJsonItem   *Json    = NULL;
    TIdHTTP       *IdHTTP1 = new TIdHTTP(NULL);
    TMemoryStream *stream  = new TMemoryStream();
    try {
        try {
            IdHTTP1->Request->Clear();
            IdHTTP1->Request->BasicAuthentication = false;
            IdHTTP1->Request->ContentType = "application/json; charset=utf8";
            IdHTTP1->Request->Accept      = "application/json; charset=utf8";
            IdHTTP1->Request->CharSet     = "utf-8";
            //IdHTTP1->Request->ContentEncoding = "utf-8";
            IdHTTP1->ConnectTimeout = 5000;
            IdHTTP1->ReadTimeout = 20000;
            IdHTTP1->AllowCookies = false;

            IdHTTP1->HTTPOptions = IdHTTP1->HTTPOptions >> hoInProcessAuth;

            //IdHTTP1->IOHandler->DefStringEncoding = TEncoding::GetEncoding(949);
            //result = IdHTTP1->Get(AApiUrl);
            //return result;
            IdHTTP1->Response->KeepAlive = false;
            IdHTTP1->Get(AApiUrl, stream);
            stream->Position = 0;
            IdHTTP1->Disconnect();
            IdHTTP1->IOHandler->InputBuffer->Clear();

            Json = new TMcJsonItem();
            Json->LoadFromStream(stream, true);
        } catch(Exception &e) {
        }
    } __finally {
        if (IdHTTP1) delete IdHTTP1;
        if (stream) delete stream;
    }
    return Json;
}
//---------------------------------------------------------------------------

TMcJsonItem* TRestObjectManager::ParseJsonString(String AJsonData)
{
    String AData = AJsonData.Trim();

    TMcJsonItem *Json = new TMcJsonItem();
    try {
        Json->AsJSON = AData;
    } catch(Exception &e) {
    }
    return Json;
}
//---------------------------------------------------------------------------

TMcJsonItem* TRestObjectManager::ParseJsonStream(TStream *AStream)
{
    TMcJsonItem *Json = new TMcJsonItem();
    try {
        Json->LoadFromStream(AStream, true);
    } catch(Exception &e) {
    }
    return Json;
}
//---------------------------------------------------------------------------

String TRestObjectManager::GetValue(TMcJsonItem *AItem, String AKey)
{
    String result = "";
    if (AItem) {
        TMcJsonItem *value = AItem->Values[AKey];
        if (value) {
            result = value->AsString;
            if (result == "null") result = "";
        }
    }
    return result;
}
//---------------------------------------------------------------------------

#if 0
    TJSONArray *jsonArr = NULL;
    try {
        reMsg->Lines->BeginUpdate();
        try {
            /*TBytes data;
            data.Length = AData.Length();
            ZeroMemory(&data[0], data.Length);
            CopyMemory(&data[0], AData.c_str(), data.Length);*/

            TStringStream *stream = new TStringStream(AData, TEncoding::UTF8);

            jsonArr = (TJSONArray*)TJSONObject::ParseJSONValue(TEncoding::UTF8->GetBytes(AData), 0);
            jsonArr = (TJSONArray*)TJSONObject::ParseJSONValue(TEncoding::UTF8->GetBytes(AData), 0);
            if (!jsonArr) {
                reMsg->Lines->Add("*******************************************: ");
                reMsg->Lines->Add("JSON Data Reading Failed: ");
                reMsg->Lines->Add("*******************************************: ");
                return;
            }

            reMsg->Lines->Add("Objects: " + String(jsonArr->Size()));
            reMsg->Lines->Add("*******************************************: ");

            cameraNmbr   = "";
            cameraId     = "";
            cameraNm     = "";
            cameraIp     = "";
            cameraStream = "";

            for (int ii = 0; ii < jsonArr->Size(); ii++) {
                TJSONObject *jo = (TJSONObject*)jsonArr->Get(ii);
                for (int jj = 0; jj < jo->Size(); jj++) {
                    TJSONPair *obj = jo->Get(jj);
                    if (obj->JsonString->Value() == CAMERA_NMBR) {
                        cameraNmbr = obj->JsonValue->Value();
                    }
                    else if (obj->JsonString->Value() == CAMERA_ID) {
                        cameraId = obj->JsonValue->Value();
                    }
                    else if (obj->JsonString->Value() == CAMERA_NM) {
                        cameraNm = obj->JsonValue->Value();
                        /*
                        String sStrVal = obj->JsonValue->Value();
                        reMsg->Lines->Add(TEncoding::Default->GetString(TEncoding::Default->GetBytes(sStrVal)));
                        reMsg->Lines->Add(TEncoding::UTF8->GetString(TEncoding::UTF8->GetBytes(sStrVal)));
                        reMsg->Lines->Add(TEncoding::UTF7->GetString(TEncoding::UTF7->GetBytes(sStrVal)));
                        reMsg->Lines->Add(TEncoding::Unicode->GetString(TEncoding::Unicode->GetBytes(sStrVal)));
                        reMsg->Lines->Add((TEncoding::UTF8->GetBytes(sStrVal)));
                        reMsg->Lines->Add((TEncoding::UTF7->GetBytes(sStrVal)));
                        reMsg->Lines->Add((TEncoding::Unicode->GetBytes(sStrVal)));
                        */
                    }
                    else if (obj->JsonString->Value() == CAMERA_IP) {
                        cameraIp = obj->JsonValue->Value();
                    }
                    else if (obj->JsonString->Value() == CAMERA_STREAM) {
                        cameraStream = obj->JsonValue->Value();
                    }
                }
                reMsg->Lines->Add(cameraNmbr + "," + cameraId + "," + cameraNm + "," + cameraIp + "," + cameraStream);

                ObjCtlrManager->SaveCameraInfo(ii+1, cameraNmbr, cameraId, cameraNm, cameraIp, cameraStream, "");
            }
        } catch(Exception &e) {
        }
    } __finally {
        if (jsonArr) jsonArr->Free();
        reMsg->Lines->EndUpdate();
    }
#endif

#if 0
reMsg->Lines->BeginUpdate();

    AData = StringReplace(AData, "[",  "",  TReplaceFlags() << rfReplaceAll);
    AData = StringReplace(AData, "]",  "",  TReplaceFlags() << rfReplaceAll);
    AData = StringReplace(AData, "},", "}", TReplaceFlags() << rfReplaceAll);
    //AData = StringReplace(AData, "{",  "",  TReplaceFlags() << rfReplaceAll);

    TStringList *pStringList = new TStringList;
    try {
        pStringList->Delimiter = '}';
        pStringList->StrictDelimiter = true;
        pStringList->DelimitedText = AData;
        for (int ii = 0; ii < pStringList->Count; ii++) {
            //reMsg->Lines->Add("*******************************************: " + String(ii+1));
            //reMsg->Lines->Add(pStringList->Strings[ii]);
        }

    } __finally {
        if (pStringList) delete pStringList;
    }


reMsg->Lines->EndUpdate();
//return;
#endif
    //String sFileName = "C:\\UTF8FileName.xml";
	//Memo1->Lines->LoadFromFile(sFileName,  TEncoding::UTF8);

    //reMsg->Lines->Clear();
    //reMsg->Lines->Add(AData);
    //String((char *)pMemoryStream->Memory, pMemoryStream->Size);
#include <OleAuto.h>
char* UTF8ToANSI(const char *pszCode)
{
    BSTR    bstrWide;
    char*   pszAnsi;
    int     nLength;

    // Get nLength of the Wide Char buffer
    nLength = MultiByteToWideChar(CP_UTF8, 0, pszCode, strlen(pszCode) + 1,
        NULL, NULL);
    bstrWide = SysAllocStringLen(NULL, nLength);

    // Change UTF-8 to Unicode (UTF-16)
    MultiByteToWideChar(CP_UTF8, 0, pszCode, strlen(pszCode) + 1, bstrWide, nLength);

    // Get nLength of the multi byte buffer
    nLength = WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, NULL, 0, NULL, NULL);
    pszAnsi = new char[nLength];

    // Change from unicode to mult byte
    WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, pszAnsi, nLength, NULL, NULL);
    SysFreeString(bstrWide);

    return pszAnsi;
}
AnsiString __fastcall Utf8ToAscii(UTF8String AUtf8)
{
    int nLen;
    WideString sUtf16;
    AnsiString sAscii = "";

    nLen = ::MultiByteToWideChar(CP_UTF8, 0, AUtf8.c_str(), AUtf8.Length(), NULL, 0);
    sUtf16.SetLength(nLen);

    ::MultiByteToWideChar(CP_UTF8, 0, AUtf8.c_str(), AUtf8.Length(), sUtf16.c_bstr(), nLen);

    nLen = ::WideCharToMultiByte(CP_ACP, 0, sUtf16.c_bstr(), sUtf16.Length(), NULL, 0, NULL, NULL);
    sAscii.SetLength(nLen);
    ::WideCharToMultiByte(CP_ACP, 0, sUtf16.c_bstr(), sUtf16.Length(), sAscii.c_str(), nLen, NULL, NULL);
    return sAscii;
}
//---------------------------------------------------------------------------

#if 0
    TJSONArray *jsonArr = NULL;
    try {
        reMsg->Lines->BeginUpdate();
        try {
            //TStringStream *stream = new TStringStream(AData, TEncoding::UTF8);

            //jsonArr = (TJSONArray*)TJSONObject::ParseJSONValue(TEncoding::UTF8->GetBytes(AnsiString(AData)), 0);
            jsonArr = (TJSONArray*)TJSONObject::ParseJSONValue(BytesOf(AData), 0);
            //jsonArr = (TJSONArray*)TJSONObject::ParseJSONValue(stream->DataString, 0);
            if (!jsonArr) {
                reMsg->Lines->Add("*******************************************: ");
                reMsg->Lines->Add("JSON Data Reading Failed: ");
                reMsg->Lines->Add("*******************************************: ");
                return;
            }

            reMsg->Lines->Add("Objects: " + String(jsonArr->Size()));
            reMsg->Lines->Add("*******************************************: ");

            cameraNmbr   = "";
            cameraId     = "";
            cameraNm     = "";
            cameraIp     = "";
            cameraStream = "";

            for (int ii = 0; ii < jsonArr->Size(); ii++) {
                TJSONObject *jo = (TJSONObject*)jsonArr->Get(ii);
                for (int jj = 0; jj < jo->Size(); jj++) {
                    TJSONPair *obj = jo->Get(jj);
                    if (obj->JsonString->Value() == CAMERA_NMBR) {
                        cameraNmbr = obj->JsonValue->Value();
                    }
                    else if (obj->JsonString->Value() == CAMERA_ID) {
                        cameraId = obj->JsonValue->Value();
                    }
                    else if (obj->JsonString->Value() == CAMERA_NM) {
                        cameraNm = obj->JsonValue->Value();
                        //RawByteString rbstr = (char*)(AnsiString(cameraNm).c_str());
                        //SetCodePage(rbstr, 949, false);
                        //cameraNm = rbstr;
                        //cameraNm= String(UTF8ToANSI(AnsiString(cameraNm).c_str()));
                        reMsg->Lines->Add(cameraNm);
                        cameraNm = (UTF8String)(obj->JsonValue->Value()).c_str();
                        /*
                        String sStrVal = obj->JsonValue->Value();
                        reMsg->Lines->Add(TEncoding::Default->GetString(TEncoding::Default->GetBytes(sStrVal)));
                        reMsg->Lines->Add(TEncoding::UTF8->GetString(TEncoding::UTF8->GetBytes(sStrVal)));
                        reMsg->Lines->Add(TEncoding::UTF7->GetString(TEncoding::UTF7->GetBytes(sStrVal)));
                        reMsg->Lines->Add(TEncoding::Unicode->GetString(TEncoding::Unicode->GetBytes(sStrVal)));
                        reMsg->Lines->Add((TEncoding::UTF8->GetBytes(sStrVal)));
                        reMsg->Lines->Add((TEncoding::UTF7->GetBytes(sStrVal)));
                        reMsg->Lines->Add((TEncoding::Unicode->GetBytes(sStrVal)));
                        */
                    }
                    else if (obj->JsonString->Value() == CAMERA_IP) {
                        cameraIp = obj->JsonValue->Value();
                    }
                    else if (obj->JsonString->Value() == CAMERA_STREAM) {
                        cameraStream = obj->JsonValue->Value();
                    }
                }
                reMsg->Lines->Add(cameraNmbr + "," + cameraId + "," + cameraNm + "," + cameraIp + "," + cameraStream);

                ObjCtlrManager->SaveCameraInfo(ii+1, cameraNmbr, cameraId, cameraNm, cameraIp, cameraStream, "");
            }
        } catch(Exception &e) {
        }
    } __finally {
        if (jsonArr) jsonArr->Free();
        reMsg->Lines->EndUpdate();
    }

#endif

