/**
 * @file task_camera.c
 * @author Shinichiro Nakamura
 * @brief カメラタスクの実装。
 */

/*
 * ===============================================================
 *  LPCXpresso Clock
 * ===============================================================
 * Copyright (c) 2010-2011 Shinichiro Nakamura
 *
 * 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 <kernel.h>
#include <t_syslog.h>
#include <t_stdlib.h>
#include <itron.h>
#include <target_syssvc.h>
#include <syssvc/serial.h>
#include "kernel_cfg.h"
#include "task_camera.h"
#include "ls_y201.h"

#define CAM1_PORTID  (SIO2_PORTID)  /**< 1カメ用ポート。 */
#define CAM2_PORTID  (SIO3_PORTID)  /**< 2カメ用ポート。 */
#define HOST_PORTID  (SIO1_PORTID)  /**< HOST用ポート。 */

#define CAMERA_CMD_CAPTURE  (0) /**< Captureコマンド。 */

#define USE_CAM1    (1)
#define USE_CAM2    (0)

/**
 * @brief カメラキャプチャコマンドに対するオプション。
 */
typedef struct {
    int camnum;
} camera_param_capture_t;

static ls_y201_t cam1;  /**< 1カメ用ハンドラ。*/
static ls_y201_t cam2;  /**< 2カメ用ハンドラ。*/

/**
 * @brief 1カメ用リード関数。
 * @param buf バッファ。
 * @param len 読み込み長さ。
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int cam1_read(unsigned char *buf, int len)
{
    return serial_rea_dat(CAM1_PORTID, (char_t *)buf, len);
}

/**
 * @brief 1カメ用ライト関数。
 * @param buf バッファ。
 * @param len 読み込み長さ。
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int cam1_write(unsigned char *buf, int len)
{
    return serial_wri_dat(CAM1_PORTID, (const char_t *)buf, len);
}

/**
 * @brief 2カメ用リード関数。
 * @param buf バッファ。
 * @param len 読み込み長さ。
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int cam2_read(unsigned char *buf, int len)
{
    return serial_rea_dat(CAM2_PORTID, (char_t *)buf, len);
}

/**
 * @brief 2カメ用ライト関数。
 * @param buf バッファ。
 * @param len 読み込み長さ。
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int cam2_write(unsigned char *buf, int len)
{
    return serial_wri_dat(CAM2_PORTID, (const char_t *)buf, len);
}

/**
 * @brief カメラキャプチャデータ読み出し用コールバック関数。
 * @param done 読み込み完了バイト数。
 * @param total 読み込み予定の総バイト数。
 * @param buf バッファ。
 * @param len 読み込み長さ。
 */
int cam_cbfunc(
        const int done,
        const int total,
        const unsigned char *buf,
        const int len)
{
    static const char_t *hextxt = (char_t *)"0123456789ABCDEF";
    int i;
    for (i = 0; i < len; i++) {
        unsigned char a, b;
        a = buf[i] / 0x10;
        b = buf[i] % 0x10;
        serial_wri_dat(HOST_PORTID, &hextxt[a], 1);
        serial_wri_dat(HOST_PORTID, &hextxt[b], 1);
        if (((i + 1) % 16) == 0) {
            static const char_t c = '\n';
            serial_wri_dat(HOST_PORTID, &c, 1);
        } else {
            static const char_t c = ' ';
            serial_wri_dat(HOST_PORTID, &c, 1);
        }
    }
    return 0;
}

/**
 * @brief キャプチャコマンドの処理。
 * @param param パラメータ。
 */
void cmd_capture(camera_param_capture_t *param)
{
    const int camnum = param->camnum;

    /*
     * 指定されたカメラ番号に応じて処理を実行する。
     */
    switch (camnum) {
        case 0:
            {
#if USE_CAM1
                /*
                 * Take picture.
                 */
                ls_y201_take_picture(&cam1);
                tslp_tsk(3000);

                /*
                 * Read the content.
                 */
                ls_y201_read_jpeg_file_content(&cam1, cam_cbfunc);

                /*
                 * Stop taking pictures.
                 */
                ls_y201_stop_taking_pictures(&cam1);
                tslp_tsk(3000);
#endif
            }
            break;
        case 1:
            {
#if USE_CAM2
                /*
                 * Take picture.
                 */
                ls_y201_take_picture(&cam2);
                tslp_tsk(3000);

                /*
                 * Read the content.
                 */
                ls_y201_read_jpeg_file_content(&cam2, cam_cbfunc);

                /*
                 * Stop taking pictures.
                 */
                ls_y201_stop_taking_pictures(&cam2);
                tslp_tsk(3000);
#endif
            }
            break;
        default:
            syslog(LOG_NOTICE, "Illegal camera number %d found.", camnum);
            break;
    }
}

/**
 * @brief カメラタスクの実装。
 * @param exinf タスクへのオプション。
 */
void task_camera(intptr_t exinf)
{
    syslog(LOG_NOTICE, "Camera task initialized.");

#if USE_CAM1
    /*
     * 1カメ用シリアルポートオープン。
     */
    serial_opn_por(CAM1_PORTID);
    serial_ctl_por(CAM1_PORTID, 0);
    LS_Y201_SETUP(&cam1, &cam1_write, &cam1_read);

    /*
     * 1カメにリセットをかける。
     */
    if (ls_y201_reset(&cam1) != NoError) {
        syslog(LOG_NOTICE, "Camera module no.1 initialize failed.");
    }
    syslog(LOG_NOTICE, "Camera no.1 info:%s", LS_Y201_VERSION(&cam1));
    tslp_tsk(4000);
#endif

#if USE_CAM2
    /*
     * 2カメ用シリアルポートオープン。
     */
    serial_opn_por(CAM2_PORTID);
    serial_ctl_por(CAM2_PORTID, 0);
    LS_Y201_SETUP(&cam2, &cam2_write, &cam2_read);

    /*
     * 2カメにリセットをかける。
     */
    if (ls_y201_reset(&cam2) != NoError) {
        syslog(LOG_NOTICE, "Camera module no.2 initialize failed.");
    }
    syslog(LOG_NOTICE, "Camera no.2 info:%s", LS_Y201_VERSION(&cam2));
    tslp_tsk(4000);
#endif

    camera_msg_t *p;
    while(1) {
        /*
         * メールボックスの状態を観察し、
         * メッセージがあれば処理を実行する。
         */
        if (rcv_mbx(MBX_CAMERA, (T_MSG **)&p) == E_OK) {
            uint8_t cmd = ((camera_msg_t *)p)->cmd;
            void *param = ((camera_msg_t *)p)->param;
            switch (cmd) {
                case CAMERA_CMD_CAPTURE:
                    cmd_capture((camera_param_capture_t *)param);
                    break;
                default:
                    syslog(LOG_NOTICE, "Unknown command 0x%x.", cmd);
                    break;
            }
            rel_mpf(MPF_CAMERA, (VP)p);
        }
    }

    syslog(LOG_NOTICE, "Camera task terminated.");
    ext_ker();
    assert(0);
}

void tskapi_camera_capture(int camnum)
{
    VP vp;

    /*
     * メモリプールからバッファを取得する。
     */
    get_mpf(MPF_CAMERA, &vp);

    /*
     * コマンドとパラメータを設定する。
     */
    ((camera_msg_t *)vp)->cmd = CAMERA_CMD_CAPTURE;
    camera_param_capture_t *param =
        (camera_param_capture_t *)&(((camera_msg_t *)vp)->param);
    param->camnum = camnum;

    /*
     * メールボックスに送信する。
     */
    snd_mbx(MBX_CAMERA, vp);

    /*
     * キャプチャの完了を適当に待つ。
     * @todo それはあんまりなのでセマフォを使うようにする。
     */
    tslp_tsk(30000);
}

