////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 2012-2019 (c) Baical                                                        /
//                                                                             /
// This library is free software; you can redistribute it and/or               /
// modify it under the terms of the GNU Lesser General Public                  /
// License as published by the Free Software Foundation; either                /
// version 3.0 of the License, or (at your option) any later version.          /
//                                                                             /
// This library is distributed in the hope that it will be useful,             /
// but WITHOUT ANY WARRANTY; without even the implied warranty of              /
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU           /
// Lesser General Public License for more details.                             /
//                                                                             /
// You should have received a copy of the GNU Lesser General Public            /
// License along with this library.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
//#define WIN32_LEAN_AND_MEAN             

#include <stdio.h>
#include <stdlib.h>

#include "GTypes.h"
#include "PSocket.h"
#include "Length.h"
#include "PString.h"
#include "Prop.h"
#include "Lock.h"
#include "PAPI.h"
#include "PAPI_Node.h"
#include "PAPI_Plugins.h"
#include "PAPI_Provider.h"
#include "PAPI_Dispatcher.h"
            
static const GUID g_sPlugin_GUID = { 0x147b2d32, 0x8b8d, 0x4695, { 0x89, 0x68, 0x9d, 0xa3, 0x27, 0x8c, 0x79, 0x22 } };

static const GUID g_sTrace_GUID = BK_GUID_STREAM_TRACE;
static const GUID g_sTelemetry_v1_GUID = BK_GUID_STREAM_TELEMETRY_V1;
static const GUID g_sTelemetry_v2_GUID = BK_GUID_STREAM_TELEMETRY_V1;


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/// <summary> Dispatcher class </summary>
class CDispatcher
        : public virtual Bk::IDispatcher
{
   tINT32 volatile m_lReference;
   Bk::IUnknown *m_iBaikal;
   Bk::IBNode *m_iNode;
   CProperty *m_pProp;
   Bk::IPluginsEnumerator *m_iPlugins;
   size_t m_szCount;
   CLock m_cLock;

public:
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    CDispatcher(Bk::IUnknown *i_iBaikal, Bk::IBNode *i_iNode, CProperty *i_pProp)
        : m_lReference(1)
        , m_iBaikal(i_iBaikal)
        , m_iNode(i_iNode)
        , m_pProp(i_pProp)
        , m_iPlugins(NULL)
        , m_szCount(0)
    {
        CLock cLock(&m_cLock);
        if (m_iNode)    
        {
            m_iNode->Add_Ref();

            //If you would like to retrieve your dispatcher configuration it is right place to do it using m_iNode interface
            //...
        }

        if (m_pProp)
        {
            m_pProp->Add_Ref();
        }

        if (m_iBaikal)
        {
            m_iBaikal->Add_Ref();
        }
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    virtual ~CDispatcher()
    {
        m_cLock.Lock();
        if (m_iNode)    
        {
            // //If you would like to save your dispatcher configuration it is right place to do it using m_iNode interface
            // //accessing dispatcher configuration node example
            // Bk::IBNode *pNode = NULL;
            // m_iNode->AddChildEmpty(L"MySubNode", &pNode);
            // if (pNode)
            // {
            //     pNode->SetAttrText(L"MyParameter", L"MyValue");
            //     pNode->Release();
            //     pNode = NULL;
            // }

            m_iNode->Release();
            m_iNode = NULL;
        }

        if (m_pProp)
        {
            m_pProp->Release();
            m_pProp = NULL;
        }

        if (m_iPlugins)
        {
            m_iPlugins->Release();
            m_iPlugins = NULL;
        }

        if (m_iBaikal)
        {
            m_iBaikal->Release();
            m_iBaikal = NULL;
        }

        m_cLock.Unlock();
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Bk::eResult __stdcall Initialize() 
    {
        CLock cLock(&m_cLock);

        if (m_iBaikal)
        {
            m_iBaikal->Query_Interface(Bk::eInterfaceStoragesEnumerator, (void*&)m_iPlugins);
            if (m_iPlugins)
            {
                m_szCount = m_iPlugins->Get_Count();
            }
        }
        return Bk::eOk;
    }


    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Bk::IPlugin* __stdcall Dispatch(const Bk::stStreamInfo &i_rStreamInfo)
    {
        CLock cLock(&m_cLock);
        Bk::IPlugin *pReturn = NULL;

        // //Check stream type: trace
        // if (0 == memcmp(&i_rStreamInfo.sType_GUID, &g_sTrace_GUID, sizeof(GUID)))
        // {
        // }
        // //Check stream type: telemetry
        // else if (0 == memcmp(&i_rStreamInfo.sType_GUID, &g_sTelemetry_v1_GUID, sizeof(GUID)))
        // {
        // }
        // else if (0 == memcmp(&i_rStreamInfo.sType_GUID, &g_sTelemetry_v2_GUID, sizeof(GUID)))
        // {
        // }

        //enumerating all storage plugins to find right one
        for (size_t szI = 0; szI < m_szCount; szI++) 
        {
            Bk::IPlugin *pPlugin = NULL;
            pPlugin = m_iPlugins->Get_Plugin(szI);
            if (pPlugin)
            {
                //if storage supports incoming stream format - this is one of the candidates, for simplicity this example return the 
                //first one, but if you have to support multiple different storage types [files, database, etc] you can choose storage
                //depending on your preferences
                if (Bk::eOk == pPlugin->Is_Stream_Supported(&i_rStreamInfo.sType_GUID))
                {
                    // //accessing storage properties example:
                    // CProperty *pRoot = NULL;
                    // pPlugin->Get_Prop(pRoot);
                    // if (pRoot)
                    // {
                    //     CProperty *pPath = NULL;
                    // 
                    //     pRoot->Get_SubItem(L"Path", pPath);
                    //     if (pPath)
                    //     {
                    //         //getting value
                    //         const wchar_t *pValue = NULL;
                    //         pPath->Get_Value(pValue);
                    //         OutputDebugStringW(pValue);
                    // 
                    //         //setting value
                    //         pPath->Set_Value(L"C:\\MyLogs");
                    //         pPath->Release();
                    //         pPath = NULL;
                    //     }
                    // 
                    //     pRoot->Release();
                    // }

                    pReturn = pPlugin;
                    break;
                }
            }
        }

        return pReturn;
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    tUINT8 __stdcall GetPriority()
    {
        //Core plugin priority is 0, so if you would like to be called - you need just apply here priority above 0, for example 1:
        return 0; //Magic replace: 3D04579F-197C-411F-8150-121613E29891
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    tINT32 __stdcall Add_Ref()
    {
        return ATOMIC_INC(&m_lReference);
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    tINT32 __stdcall Release()
    {
        tINT32 lResult = ATOMIC_DEC(&m_lReference);
        if ( 0 >= lResult )
        {
            delete this;
        }

        return lResult;
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Bk::eResult __stdcall Query_Interface(Bk::eInterface i_eId, void *&o_rUnknown)
    {
        UNUSED_ARG(i_eId);
        UNUSED_ARG(o_rUnknown);
        return Bk::eErrorNotImplemented;
    }
};

extern "C" 
{
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/// <summary>Provides plugin information</summary>
/// <param name="o_pInfo"> plugin information, output value</param>
/// <returns>Bk::eResult __stdcall</returns>
P7_EXPORT Bk::eResult __cdecl Get_Plugin_Info(Bk::stPluginInfo *o_pInfo)
{
    Bk::eResult eResult = Bk::eOk;

    if (NULL == o_pInfo)
    {
        return Bk::eErrorWrongInput;
    }

    memcpy(&o_pInfo->sGUID, &g_sPlugin_GUID, sizeof(GUID));

    o_pInfo->eType         = Bk::ePluginStreamsDispatcher;
    o_pInfo->dwAPI_Version = BK_PLUGIN_API_VERSION;
    o_pInfo->qwVersion     = 0;
    o_pInfo->pFormats      = NULL;

    PStrCpy(o_pInfo->pName, LENGTH(o_pInfo->pName), TM("Dispatcher: Core")); //Magic replace: 9C0CBC62-930B-453D-8773-D6759A427FC7

    return eResult;
}//Get_Plugin_Info

//compiler check than function match the prototype
static const fnBkGetPluginInfo g_pGetPluginInfo = Get_Plugin_Info;


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/// <summary>Create dispatcher class instance</summary>
/// <param name="i_pBaikal"> Baikal main interface</param>
/// <param name="i_pNode"> Confoguration node of dispatcher plugin</param>
/// <param name="o_rDispatcher"> resulting dispatcher interface</param>
/// <returns>Bk::eResult::eOk in case of success</returns>
/// <remarks></remarks>
P7_EXPORT Bk::eResult __cdecl Create_Dispatcher(Bk::IUnknown     *i_pBaikal,     //[inp] Baikal interface
                                                Bk::IBNode       *i_pNode,       //[inp] Dispatcher config. node
                                                CProperty        *i_pProp,       //[Inp] properties
                                                Bk::IDispatcher *&o_rDispatcher  //[out] Dispatcher interface
                                               )
{
    o_rDispatcher = new CDispatcher(i_pBaikal, i_pNode, i_pProp);
    return Bk::eOk;
}


//compiler check than function match the prototype
static const fnBkCreateDispatcher g_pCreateDispatcher = Create_Dispatcher;
}//extern "C"


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//Dll entry point
#if defined(_WIN32) || defined(_WIN64)
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
    {
        switch (ul_reason_for_call)
        {
            case DLL_PROCESS_ATTACH:
            case DLL_THREAD_ATTACH:
            case DLL_THREAD_DETACH:
            case DLL_PROCESS_DETACH:
            {
                break;
            }
        }
        return TRUE;
    }
#endif

