//#DOC Main, program entry and main loop
#include "main.h"
#include <otFPGA.h>
#include <otPort.h>
#include <otSpi.h>
#include <otI2cBus.h>
#include <otSerial.h>
#include <otTimer.h>
#include <otPrintf.h>
#include <otArduCAM.h>
#include <otMalloc.h>
#include <otFILE.h>
#include <otImageFile.h>

U32     fileCounter = 0;

otArduCAM   cam;
otFILE      file;
otImageFile imgf;

void grabMovie()
{
    U32     len, pt = 0, et, i = 0;
    char    buf[32];
    sprintf(buf, "mov%04u.avi", fileCounter++);
    struct avi_t * avi = imgf.openAVI(buf, cam.width(), cam.height());
    printf("Press any key to stop ...\n");
    ElapsedTimeStart(TIMER0);
    while(UartCheckChar(UART0) == false)
    {
        len = cam.grab();
        et = ElapsedTimeEnd(TIMER0);
        printf("Grab: %4u %6u ms,\t", i, et - pt);
        pt = et;
        imgf.addFrameAVI(avi, (const U8 *) cam.buffer(), len);
        et = ElapsedTimeEnd(TIMER0);
        printf("Save: %6u ms\n", et - pt);
        pt = et;
        i++;
    }
    UartChar(UART0);
    F32 ms = ElapsedTimeEnd(TIMER0);
    ms = 1000.0f / (ms / i);
    imgf.setFrameRateAVI(avi, ms);
    imgf.closeAVI(avi);
    printf("End of movie grab, %f fps\n", ms);
}

void grabShot()
{
    U32     et;
    char    tmpBuff[32];
    ElapsedTimeStart(TIMER0);
    U32 len = cam.grab();
    et = ElapsedTimeEnd(TIMER0);
    sprintf(tmpBuff, "I%07d.JPG", fileCounter++);
    FILE fd = file.fopen(tmpBuff, otFILE::A_CREATE_ALWAYS | otFILE::A_WRITE);
    if(fd)
    {
        file.fsave(fd, (const char *) cam.buffer(), len);
        file.fclose(fd);
        printf("Grab time: %4u ms, Size: %6u bytes, File: %s\n", et, len, tmpBuff);
    }
}

void setResolution()
{
    printf("0  320x240\n");
    printf("1  640x480\n");
    printf("2 1024x768\n");
    printf("3 1280x960\n");
    printf("4 1600x1200\n");
    printf("5 2048x1536\n");
    printf("6 2592x1944\n");
    printf("7 1920x1080\n");
    printf("Specify the resolution: ");
    
    U8 ch = UartGetChar(UART0);
    cam.setSize(ch - 0x30);
    printf("\n");
    // Set again flip and mirror some settings change it
    cam.setMirrorFlip(cam.mirrorFlip());
    cam.setJpegQuality(cam.jpegQuality());
}

void setImageQuality()
{
    printf("0 - High\n");
    printf("1 - Normal\n");
    printf("2 - Low\n");
    printf("> ");
    U8 ch = UartGetChar(UART0);
    cam.setJpegQuality((otArduCAM::JpegQuality) ch);
    printf("\n");
}

void Menu()
{
    printf("Commands:\n");
    printf("G - Grab a shot\n");
    printf("M - Grab a movie\n");
    printf("Q - Set image quality\n");
    printf("R - Set resolution\n");
    printf("> ");
}

void setup()
{
    // Setup the UART0 used by printf
    UartEnableSignals(UART0);
    UartSetBaud(UART0, 115200);
    
    TimerSetAsCounter(TIMER0);
    TimerEnable(TIMER0, true);
    
    // Init printf
    setStdout(4096, UART0); // printf output is now directed on UART0
    // Init the FPGA, slave selection are made with it
    FpgaInit();
    FpgaHiddenPort(HP_EXT_I2C, true);
    
    I2cBusReset();
    I2cBusFrequency(100, 30);
    
    DelayMs(1000);
    
    // Setup a SPI channel
    SpiChannel(SA_Y15, 5, 0, 0);
    SpiSet(SA_Y15);
    
    printf("ArduCAM grab & save test program. Rev 0.0.2\n");
    
    if(file.init())
    {
        file.setStdout(printf);
        printf("Format SD (Y/N) ?  ");
        char ch = UartGetChar(UART0);
        stdOutLF();
        if(ch == 'y' || ch == 'Y')
        {
            // Show format progress
            file.showWriteActivity(true);
            // Format SD
            if(!file.format("SD-DISK"))
                goto error;
            file.showWriteActivity(false);
        }
        // Display free SD space
        printf("Disk free: %u Kbytes\n", file.free());
    }
    else
    {
error:
        // Display error message
        printf("STATUS: %s\n", file.status());
        // Unmount and turn off SD
        file.end();
        DelayMs(100);
        HIBE;
    }
    
    imgf.init(&file);
    
    if(!cam.init(otArduCAM::OV5642, otArduCAM::IF_JPEG))
    {
        printf("ArduCAM not found!\n");
        IDLE;
    }
    cam.setSize(otArduCAM::OV5642_640x480);
    cam.setMirrorFlip(otArduCAM::P_FLIP);
}

void loop()
{
    Menu();
    U8 ch = UartGetChar(UART0);
    printf("\n");
    switch(ch)
    {
        case 'G':   grabShot();
                    break;
        case 'M':   grabMovie();
                    break;
        case 'Q':   setImageQuality();
                    break;
        case 'R':   setResolution();
                    break;
    }
}