#include "stdio.h"
#include "windows.h"
#include "MediaInfoDLL/MediaInfoDLL.h"
#include "MediaInfo/MediaInfo_Events.h"

/***************************************************************************/
/* Example of demuxing                                                     */
/***************************************************************************/

/*You can define your own structure*/
struct UserHandle_struct
{
    void* MI;
    const char* FileName;

    /*Global File handler in order not to open them every time */
    struct filehandlerfromids
    {
        #define StreamIDs_Max 100 //Arbitrary count of maximum stream IDs
        MediaInfo_int64u StreamIDs[StreamIDs_Max]; 
        HANDLE FileHandler; 
    };
    #define FileHandlerFromIDs_Max 100 //Arbitrary count of maximum outputs
    struct filehandlerfromids FileHandlerFromIDs[FileHandlerFromIDs_Max];
};


void Global_Demux_0 (struct MediaInfo_Event_Global_Demux_0* Event, struct UserHandle_struct* UserHandler)
{
    /***********************************************************************/
    /* This is only an example, do wat you want with the buffer            */
    /***********************************************************************/

    size_t FileHandlerFromIDs_Pos;
    DWORD Content_Size_Written;
    unsigned char ParserID;

    /*Getting the parser identifier, if you want to know at wich demux level you are*/
    ParserID    =(unsigned char) ((Event->EventCode&0xFF000000)>>24);
    /*if (ParserID!=MediaInfo_Parser_MpegPs)*/ /*Example to test you are at the PES level*/
    /*    return;*/
    
    /*Testing if we already open the file*/
    for (FileHandlerFromIDs_Pos=0; FileHandlerFromIDs_Pos<FileHandlerFromIDs_Max; FileHandlerFromIDs_Pos++)
    {
        int IsFound=1;
        int IsEmpty=1;
        size_t StreamIDs_Pos;
        for (StreamIDs_Pos=0; StreamIDs_Pos<Event->StreamIDs_Size; StreamIDs_Pos++)
        {
            if (UserHandler->FileHandlerFromIDs[FileHandlerFromIDs_Pos].StreamIDs[StreamIDs_Pos]!=Event->StreamIDs[StreamIDs_Pos])
                IsFound=0;
            if (UserHandler->FileHandlerFromIDs[FileHandlerFromIDs_Pos].StreamIDs[StreamIDs_Pos]!=(MediaInfo_int64u)-1)
                IsEmpty=0;
        }

        if (IsEmpty!=0)
        {
            /*There is no already open file, creating one*/
            size_t FileName_Size=strlen(UserHandler->FileName);
            size_t StreamIDs_Pos=0;
            char FileName[10000]; /*need some place, arbitrary size*/
            if (FileName_Size>10000-100)
                return;
            strcpy(FileName, UserHandler->FileName);
            for (StreamIDs_Pos=0; StreamIDs_Pos<Event->StreamIDs_Size; StreamIDs_Pos++)
            {
                MediaInfo_int8u Pos=0;
                FileName[FileName_Size]='.';
                FileName_Size++;
                for (Pos=0; Pos<Event->StreamIDs_Width[StreamIDs_Pos]; Pos++)
                {
                    /*Adding StreamID, in Hexa*/
                    MediaInfo_int8u StreamID_1Byte=(MediaInfo_int8u)((Event->StreamIDs[StreamIDs_Pos]>>((Event->StreamIDs_Width[StreamIDs_Pos]-1-Pos)*4))&0xFF);
                    char ToAdd=((StreamID_1Byte&0x0F)>=0xA)?('A'+(StreamID_1Byte&0x0F)-10):('0'+(StreamID_1Byte&0x0F));
                    FileName[FileName_Size]=ToAdd;
                    FileName_Size++;

                    /*Registering the ID*/
                    UserHandler->FileHandlerFromIDs[FileHandlerFromIDs_Pos].StreamIDs[StreamIDs_Pos]=Event->StreamIDs[StreamIDs_Pos];
                }
            }
            FileName[FileName_Size]='\0';

            UserHandler->FileHandlerFromIDs[FileHandlerFromIDs_Pos].FileHandler=CreateFile(FileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
            if (UserHandler->FileHandlerFromIDs[FileHandlerFromIDs_Pos].FileHandler==INVALID_HANDLE_VALUE)
            {
                printf("Can not open output file %s\n, FileName");
                return;
            }
            break;
        }

        if (IsFound!=0)
        {
            break;
        }
    }

    /*Writing file*/
    WriteFile(UserHandler->FileHandlerFromIDs[FileHandlerFromIDs_Pos].FileHandler, Event->Content, Event->Content_Size, &Content_Size_Written, NULL);
}

/***************************************************************************/
/* The callback function                                                   */
/***************************************************************************/

void __stdcall Event_CallBackFunction(unsigned char* Data_Content, size_t Data_Size, void* UserHandler_Void)
{
    /*Retrieving UserHandler*/
    struct UserHandle_struct*           UserHandler=(struct UserHandle_struct*)UserHandler_Void;
    struct MediaInfo_Event_Generic*     Event_Generic=(struct MediaInfo_Event_Generic*) Data_Content;
    unsigned char                       ParserID;
    unsigned short                      EventID;
    unsigned char                       EventVersion;

    /*integrity tests*/
    if (Data_Size<4)
        return; //There is a problem

    /*Do what you need with the event, here is only an example*/
    /*Retrieving EventID*/
    ParserID    =(unsigned char) ((Event_Generic->EventCode&0xFF000000)>>24);
    EventID     =(unsigned short)((Event_Generic->EventCode&0x00FFFF00)>>8 );
    EventVersion=(unsigned char) ( Event_Generic->EventCode&0x000000FF     );

    /*Global to all parsers*/
    switch (EventID)
    {
        case MediaInfo_Event_Global_Demux                                           : if (EventVersion==0 && Data_Size==sizeof(struct MediaInfo_Event_Global_Demux_0)) Global_Demux_0((struct MediaInfo_Event_Global_Demux_0*)Data_Content, UserHandler); break;
        default                                                                     : ;
    }
}

/***************************************************************************/
/* Parsing the file, example with the stream interface                     */
/***************************************************************************/

int MI_Parse (void* MI, const char* FileName)
{
    /*Note: You can use basicly the analyze by FileName too, the code her is to demonstrate the usage of buffers, useful if you capture a stream*/
    HANDLE From;
    unsigned char* From_Buffer;
    DWORD From_Buffer_Size;
    DWORD From_Size_Low; 
    DWORD From_Size_High;
    unsigned long long From_Size;

    /***********************************************************************/
    /* Preparing input file                                                */
    /***********************************************************************/

    /*From: preparing an example file for reading*/
    From=CreateFile(FileName, FILE_READ_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    if (From==INVALID_HANDLE_VALUE)
    {
        printf("Can not open input file\n");
        return 1;
    }
    From_Size_Low=GetFileSize(From, &From_Size_High);
    if (From_Size_Low==INVALID_FILE_SIZE && GetLastError()!=NO_ERROR)
    {
        printf("Can not get input file size\n");
        return 1;
    }
    From_Size=0x100000000ULL*From_Size_High+From_Size_Low;

    /*From: preparing a memory buffer for reading*/
    From_Buffer=(unsigned char*)malloc(1500*188); //Note: you can do your own buffer

    /*Preparing to fill MediaInfo with a buffer*/
    MediaInfo_Open_Buffer_Init(MI, From_Size, 0);

    /***********************************************************************/
    /* Parsing                                                             */
    /***********************************************************************/

    /*The parsing loop*/
    do
    {
        /*Reading data somewhere, do what you want for this.*/
        if (!ReadFile(From, From_Buffer, 1500*188, &From_Buffer_Size, NULL))
        {
            /*Clean up*/
            free(From_Buffer);

            printf("Can not read input file\n");
            return 1;
        }

        /*Sending the buffer to MediaInfo*/
        MediaInfo_Open_Buffer_Continue(MI, From_Buffer, From_Buffer_Size);

        /*Managing seek requests*/
        if (MediaInfo_Open_Buffer_Continue_GoTo_Get(MI)!=(MediaInfo_int64u)-1)
        {
            LARGE_INTEGER GoTo; GoTo.QuadPart=MediaInfo_Open_Buffer_Continue_GoTo_Get(MI);
            GoTo.LowPart=SetFilePointer(From, GoTo.LowPart, &GoTo.HighPart, FILE_BEGIN);
            if (GoTo.LowPart==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR)
            {
                /*Clean up*/
                free(From_Buffer);

                printf("Error during input file seek\n");
                return 1;
            }
            MediaInfo_Open_Buffer_Init(MI, From_Size, GoTo.QuadPart);
        }
    }
    while (From_Buffer_Size>0);
    MediaInfo_Open_Buffer_Finalize(MI);

    /*Clean up*/
    free(From_Buffer);

    return 0;
}

/***************************************************************************/
/* Main                                                                    */
/***************************************************************************/

int main (int argc, char *argv[])
{
    void* MI;
    char Event_CallBackFunction_Text[1000];
    const char* MI_Result;
    const char* FileName=NULL;
    int argv_Problem=0;
    struct UserHandle_struct UserHandle;
    size_t FileHandlerFromIDs_Pos;

    /***********************************************************************/
    /* Init                                                                */
    /***********************************************************************/

    for (FileHandlerFromIDs_Pos=0; FileHandlerFromIDs_Pos<FileHandlerFromIDs_Max; FileHandlerFromIDs_Pos++)
    {
        size_t StreamIDs_Pos;
        for (StreamIDs_Pos=0; StreamIDs_Pos<StreamIDs_Max; StreamIDs_Pos++)
            UserHandle.FileHandlerFromIDs[FileHandlerFromIDs_Pos].StreamIDs[StreamIDs_Pos]=(MediaInfo_int64u)-1;
    }

    /***********************************************************************/
    /* Options handling                                                    */
    /***********************************************************************/

    /*Input*/
    if (argc!=2)
    {
        printf("Usage: MediaInfo_Demux_DCA FileName\n");
        return 1;
    }
    FileName=argv[1];
    UserHandle.FileName=FileName;
    
    /***********************************************************************/
    /* MediaInfo library init                                              */
    /***********************************************************************/

    /*Initializing MediaInfo*/
    if (MediaInfoDLL_Load()!=1)
    {
        printf("MediaInfo error: MediaInfo.dll not found\n");
        return 1;
    }

    /***********************************************************************/
    /* Parsing, with events                                                */
    /***********************************************************************/

    /*Init*/
    MI=MediaInfo_New();
    if (MI==NULL)
    {
        printf("MediaInfo error: impossible to create a new MediaInfo handler\n");
        return 1;
    }

    /*CallBack configuration*/
    /*MediaInfo need pointer as text, for compatibility with older version) + 64-bit OS handling*/
    /*form is "CallBack=memory://handlerInDecimal;UserHandler=memory://handlerInDecimal"*/
    /*UserHandler is a unique value wich will be provided to the callback function, in order to know which MediaInfo instance send the event*/
    UserHandle.MI=MI;
    strcpy(Event_CallBackFunction_Text, "CallBack=memory://");
    _ui64toa_s((unsigned __int64)Event_CallBackFunction, Event_CallBackFunction_Text+strlen(Event_CallBackFunction_Text), 1000-strlen(Event_CallBackFunction_Text), 10);
    strcpy(Event_CallBackFunction_Text+strlen(Event_CallBackFunction_Text), ";UserHandler=memory://");
    _ui64toa_s((unsigned __int64)&UserHandle, Event_CallBackFunction_Text+strlen(Event_CallBackFunction_Text), 1000-strlen(Event_CallBackFunction_Text), 10);
    MI_Result=MediaInfo_Option(MI, "File_Event_CallBackFunction", Event_CallBackFunction_Text);
    if (strlen(MI_Result))
    {
        printf("MediaInfo error: %s\r\n", MI_Result);
        return 1;
    }

    /*We want all the stream, not only the beginning*/
    MI_Result=MediaInfo_Option(MI, "ParseSpeed", "1.0");
    if (strlen(MI_Result))
    {
        printf("MediaInfo error: %s\n", MI_Result);
        return 1;
    }
    
    /*We want to demux*/
    MI_Result=MediaInfo_Option(MI, "Demux", "all");
    if (strlen(MI_Result))
    {
        printf("MediaInfo error: %s\n", MI_Result);
        return 1;
    }
    
    /*Parsing*/
    /*if (MI_Parse (MI, FileName))*/ /*"By buffer" parsing is not adapted for SSIF files, using "File" parsing, if you need "By buffer" parsing with SSIF, let me know*/
    if (MediaInfo_Open(MI, FileName)==0)
        return 1;

    /*Clean up*/
    MediaInfo_Delete(MI);

    return 0;
}
