/**
 * ======================================================================
 * LS-Y201 device driver (Version 0.0.1)
 * Reference documents:
 * LinkSprite JPEG Color Camera Serial UART Interface January 2010
 * ======================================================================
 * Copyright (c) 2010 Shinichiro Nakamura (CuBeatSystems)
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ======================================================================
 */

#include "ls_y201.h"

static ErrorCode waitInitEnd(ls_y201_t *p);

/**
 * Reset module.
 *
 * @return Error code.
 */
ErrorCode ls_y201_reset(ls_y201_t *p)
{
    unsigned char send[4] = {
        0x56,
        0x00,
        0x26,
        0x00
    };
    unsigned char recv[4];

    /*
     * Sometime, The camera transmit so many data bytes for long time.
     * So we should read data before reset command send.
     */
    // @todo

    if (!p->write(send, sizeof(send))) {
        return SendError;
    }
    if (!p->read(recv, sizeof(recv))) {
        return RecvError;
    }
    if ((recv[0] == 0x76)
            && (recv[1] == 0x00)
            && (recv[2] == 0x26)
            && (recv[3] == 0x00)) {
        ErrorCode r = waitInitEnd(p);
        if (r != NoError) {
            return r;
        }
        return NoError;
    } else {
        return UnexpectedReply;
    }
}

/**
 * Set image size.
 *
 * @param is Image size.
 * @return Error code.
 */
ErrorCode ls_y201_set_image_size(ls_y201_t *p, ImageSize is)
{
    unsigned char send[9] = {
        0x56,
        0x00,
        0x31,
        0x05,
        0x04,
        0x01,
        0x00,
        0x19,
        0x00    // 0x11:320x240, 0x00:640x480, 0x22:160x120
    };
    unsigned char recv[5];
    switch (is) {
        case ImageSize160x120:
            send[8] = 0x22;
            break;
        case ImageSize320x280:
            send[8] = 0x11;
            break;
        case ImageSize640x480:
            send[8] = 0x00;
            break;
        default:
            return InvalidArguments;
    }
    if (!p->write(send, sizeof(send))) {
        return SendError;
    }
    if (!p->read(recv, sizeof(recv))) {
        return RecvError;
    }
    if ((recv[0] == 0x76)
            && (recv[1] == 0x00)
            && (recv[2] == 0x31)
            && (recv[3] == 0x00)
            && (recv[4] == 0x00)) {
        return ls_y201_reset(p);
    } else {
        return UnexpectedReply;
    }
}

/**
 * Take picture.
 *
 * @return Error code.
 */
ErrorCode ls_y201_take_picture(ls_y201_t *p)
{
    unsigned char send[5] = {
        0x56,
        0x00,
        0x36,
        0x01,
        0x00
    };
    unsigned char recv[5];

    if (!p->write(send, sizeof(send))) {
        return SendError;
    }
    if (!p->read(recv, sizeof(recv))) {
        return RecvError;
    }

    if ((recv[0] == 0x76)
            && (recv[1] == 0x00)
            && (recv[2] == 0x36)
            && (recv[3] == 0x00)
            && (recv[4] == 0x00)) {
        /*
         * I think the camera need a time for operating.
         * But there is no any comments on the documents.
         */
        // @todo
        return NoError;
    } else {
        return UnexpectedReply;
    }
}

/**
 * Read jpeg file size.
 *
 * @param fileSize File size.
 * @return Error code.
 */
ErrorCode ls_y201_read_jpeg_file_size(ls_y201_t *p, int *fileSize)
{
    unsigned char send[5] = {
        0x56,
        0x00,
        0x34,
        0x01,
        0x00
    };
    unsigned char recv[9];

    if (!p->write(send, sizeof(send))) {
        return SendError;
    }
    if (!p->read(recv, sizeof(recv))) {
        return RecvError;
    }

    if ((recv[0] == 0x76)
            && (recv[1] == 0x00)
            && (recv[2] == 0x34)
            && (recv[3] == 0x00)
            && (recv[4] == 0x04)
            && (recv[5] == 0x00)
            && (recv[6] == 0x00)) {
        *fileSize = ((recv[7] & 0x00ff) << 8)
                    | ((recv[8] & 0x00ff) << 0);
        return NoError;
    } else {
        return UnexpectedReply;
    }
}

/**
 * Read jpeg file content.
 *
 * @param func A pointer to a call back function.
 * @return Error code.
 */
ErrorCode ls_y201_read_jpeg_file_content(
        ls_y201_t *p,
        int (*func)(
            const int done,
            const int total,
            const unsigned char *buf,
            const int siz))
{
    unsigned char send[16] = {
        0x56,
        0x00,
        0x32,
        0x0C,
        0x00,
        0x0A,
        0x00,
        0x00,
        0x00, // MH
        0x00, // ML
        0x00,
        0x00,
        0x00, // KH
        0x00, // KL
        0x00, // XX
        0x00  // XX
    };
    unsigned char body[32];
    unsigned short m = 0; // Staring address.
    unsigned short k = sizeof(body); // Packet size.
    unsigned short x = 10;    // Interval time. XX XX * 0.01m[sec]
    int endflag = 0;

    /*
     * Get the data size.
     */
    int siz_done = 0;
    int siz_total = 0;
    ErrorCode r = ls_y201_read_jpeg_file_size(p, &siz_total);
    if (r != NoError) {
        return r;
    }

    do {
        send[8] = (m >> 8) & 0xff;
        send[9] = (m >> 0) & 0xff;
        send[12] = (k >> 8) & 0xff;
        send[13] = (k >> 0) & 0xff;
        send[14] = (x >> 8) & 0xff;
        send[15] = (x >> 0) & 0xff;
        /*
         * Send a command.
         */
        if (!p->write(send, sizeof(send))) {
            return SendError;
        }
        /*
         * Read the header of the response.
         */
        unsigned char header[5];
        if (!p->read(header, sizeof(header))) {
            return RecvError;
        }
        /*
         * Check the response and fetch an image data.
         */
        if ((header[0] == 0x76)
                && (header[1] == 0x00)
                && (header[2] == 0x32)
                && (header[3] == 0x00)
                && (header[4] == 0x00)) {
            int i;
            if (!p->read(body, sizeof(body))) {
                return RecvError;
            }
            siz_done += sizeof(body);
            if (func != 0) {
                if (siz_done > siz_total) {
                    siz_done = siz_total;
                }
                func(siz_done, siz_total, body, sizeof(body));
            }
            for (i = 1; i < sizeof(body); i++) {
                if ((body[i - 1] == 0xFF) && (body[i - 0] == 0xD9)) {
                    endflag = 1;
                }
            }
        } else {
            return UnexpectedReply;
        }
        /*
         * Read the footer of the response.
         */
        unsigned char footer[5];
        if (!p->read(footer, sizeof(footer))) {
            return RecvError;
        }

        m += sizeof(body);
    } while (endflag == 0);
    return NoError;
}

/**
 * Stop taking pictures.
 *
 * @return Error code.
 */
ErrorCode ls_y201_stop_taking_pictures(ls_y201_t *p)
{
    unsigned char send[5] = {
        0x56,
        0x00,
        0x36,
        0x01,
        0x03
    };
    unsigned char recv[5];

    if (!p->write(send, sizeof(send))) {
        return SendError;
    }
    if (!p->read(recv, sizeof(recv))) {
        return RecvError;
    }

    if ((recv[0] == 0x76)
            && (recv[1] == 0x00)
            && (recv[2] == 0x36)
            && (recv[3] == 0x00)
            && (recv[4] == 0x00)) {
        /*
         * I think the camera need a time for operating.
         * But there is no any comments on the documents.
         */
        // @todo
        return NoError;
    } else {
        return UnexpectedReply;
    }
}

/**
 * Wait init end codes.
 *
 * @return True if the data sended.
 */
static ErrorCode waitInitEnd(ls_y201_t *p) {
    char PWR_ON_MSG[10] = {
        'I',
        'n',
        'i',
        't',
        ' ',
        'e',
        'n',
        'd',
        0x0d,
        0x0a
    };

    {
        int i;
        for (i = 0; i < sizeof(p->version); i++) {
            p->version[i] = '\0';
        }
    }

    {
        int i;
        int cnt = 0;
        for (i = 0; i < sizeof(PWR_ON_MSG); i++) {
            unsigned char c = 0x00;
            do {
                if (!p->read(&c, sizeof(c))) {
                    return RecvError;
                }
                /*
                 * What is the version of the camera?
                 * You can check the version with the version field.
                 *
                 * VC0703 1.00
                 * Ctrl infr exist
                 * User-defined sensor
                 * 625
                 * Init end
                 */
                if (cnt < sizeof(p->version) - 1) {
                    if (c != 0x00) {
                        p->version[cnt] = c;
                        cnt++;
                    }
                } else {
                    return UnexpectedReply;
                }
            } while (c != PWR_ON_MSG[i]);
        }
    }
    return NoError;
}

