//  Copyright (C) 2016, mituhiromatuura all right reserved.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <kernel.h>
#include "kernel_cfg.h"

#include "beep.h"
#include "button.h"
#include "disp.h"
#include "exti.h"
#include "fatfs.h"
#include "gps.h"
#include "index.h"
#include "lcd.h"
#include "rec_avi.h"
#include "save_jpg.h"
#include "wait.h"

#define LOAD_UI(a, b) {uip = (unsigned int*)&a; *uip = b;}
#define LOAD_US(a, b) {usp = (unsigned short*)&a; *usp = b;}

extern void write_JPEG_file (FIL *fobjp, int quality);

extern volatile int filenum;
extern FIL f_gps;
extern int save_gps;
extern int sensor_g[3];

unsigned char avi_head[0x162e];

static unsigned int fcc(char cc[])
{
	int n;
	unsigned int res = 0;

	for(n = 3; n > -1; n--)
	{
		res <<= 8;
		res |= cc[n];
	}
	return res;
}

void rec_avi(void)
{
	if(0 != (disk_initialize(0) & (STA_NOINIT | STA_NODISK)))goto error;

	FIL f_avi;
	FIL f_siz;
	FRESULT res;
	UINT size;

	res = fatfs_mount(&fatfs, "", 0);
	if(FR_OK != res)goto error;

	char f_name_avi[128];
	char f_name_siz[128];
	char f_name_gps[128];

	while(1)
	{
		sprintf(f_name_avi, "%d.avi", filenum);
		sprintf(f_name_siz, "%d.siz", filenum);
		sprintf(f_name_gps, "%d.nmea", filenum);
		if((FR_NO_FILE == fatfs_stat(f_name_avi, NULL))
		&& (FR_NO_FILE == fatfs_stat(f_name_siz, NULL))
		&& (FR_NO_FILE == fatfs_stat(f_name_gps, NULL)))
		{
			break;
		}
		filenum++;
	}

	res = fatfs_open(&f_gps, f_name_gps, FA_WRITE | FA_CREATE_NEW);
	if(FR_OK != res)goto error;
	save_gps = 1;

	res = fatfs_open(&f_siz, f_name_siz, FA_WRITE | FA_CREATE_NEW);
	if(FR_OK != res)goto error;

	res = fatfs_open(&f_avi, f_name_avi, FA_WRITE | FA_CREATE_NEW);
	if(FR_OK != res)goto error;

	memset(avi_head, 0, sizeof(avi_head));

	unsigned int	*uip;
	unsigned short	*usp;

	LOAD_UI(avi_head[0x0000], fcc("RIFF"));		//ファイルタイプ
//	LOAD_UI(avi_head[0x0004], 0);					//ファイルタイプから数えたRIFFファイルのサイズ
	LOAD_UI(avi_head[0x0008], fcc("AVI "));			//ファイルタイプ
	LOAD_UI(avi_head[0x000c], fcc("LIST"));				//リストチャンク
	LOAD_UI(avi_head[0x0010], 0x1622-0x0014);			//リストサイズ
	LOAD_UI(avi_head[0x0014], fcc("hdrl"));				//リストタイプ
	LOAD_UI(avi_head[0x0018], fcc("avih"));					//AVIメインヘッダ
	LOAD_UI(avi_head[0x001c], 0x0058-0x0020);				//サイズ
//	LOAD_UI(avi_head[0x0020], 0);							//フレーム間隔
//	LOAD_UI(avi_head[0x0024], 0);							//毎秒当たりの最大データ・レート
	LOAD_UI(avi_head[0x0028], 0);							//データ整列の基数
	LOAD_UI(avi_head[0x002c], 0x00000010);					//フラグ
//	LOAD_UI(avi_head[0x0030], fnum);						//ファイル内のフレームの総数
	LOAD_UI(avi_head[0x0034], 0);							//インタリーブ開始フレーム
	LOAD_UI(avi_head[0x0038], 1);							//ファイル内のストリーム種類数
	LOAD_UI(avi_head[0x003c], 0);							//必要バッファサイズ
	LOAD_UI(avi_head[0x0040], 320);							//画面幅
	LOAD_UI(avi_head[0x0044], 240);							//画面高さ
	LOAD_UI(avi_head[0x0048], 0);							//予約済み
	LOAD_UI(avi_head[0x004c], 0);
	LOAD_UI(avi_head[0x0050], 0);
	LOAD_UI(avi_head[0x0054], 0);
	LOAD_UI(avi_head[0x0058], fcc("LIST"));				//リストチャンク
	LOAD_UI(avi_head[0x005c], 0x10f4-0x0060);			//リストサイズ
	LOAD_UI(avi_head[0x0060], fcc("strl"));				//リストタイプ
	LOAD_UI(avi_head[0x0064], fcc("strh"));					//AVIストリームヘッダ
	LOAD_UI(avi_head[0x0068], 0x00a4-0x006c);				//最初の8バイト分を除くサイズ
	LOAD_UI(avi_head[0x006c], fcc("vids"));					//ストリームのデータ・タイプ
	LOAD_UI(avi_head[0x0070], fcc("MJPG"));
	LOAD_UI(avi_head[0x0074], 0);
	LOAD_UI(avi_head[0x0078], 0);
	LOAD_UI(avi_head[0x007c], 0);
//	LOAD_UI(avi_head[0x0080], 0);							//ストリームの単位時間
	LOAD_UI(avi_head[0x0084], 1000000);						//単位時間当たりのサンプル数
	LOAD_UI(avi_head[0x0088], 0);							//ストリームの開始時間
//	LOAD_UI(avi_head[0x008c], fnum);						//ストリームの長さ
	LOAD_UI(avi_head[0x0090], 0x2b70);
	LOAD_UI(avi_head[0x0094], -1);
	LOAD_UI(avi_head[0x0098], 0);
	LOAD_US(avi_head[0x009c], 0);
	LOAD_US(avi_head[0x009e], 0);
	LOAD_US(avi_head[0x00a0], 320);
	LOAD_US(avi_head[0x00a2], 240);
	LOAD_UI(avi_head[0x00a4], fcc("strf"));					//ストリーム・フォーマット
	LOAD_UI(avi_head[0x00a8], 0x00d4-0x00ac);				//サイズ
	LOAD_UI(avi_head[0x00ac], 0x00d4-0x00ac);				//情報ヘッダのサイズ
	LOAD_UI(avi_head[0x00b0], 320);							//画像の幅
	LOAD_UI(avi_head[0x00b4], 240);							//画像の高さ
	LOAD_US(avi_head[0x00b8], 1);							//プレーン数
	LOAD_US(avi_head[0x00ba], 24);							//１画素あたりのデータサイズ
	LOAD_UI(avi_head[0x00bc], fcc("MJPG"));					//圧縮形式
	LOAD_UI(avi_head[0x00c0], 320*240*3);					//画像データのサイズ（0でデフォルト）
	LOAD_UI(avi_head[0x00c4], 0);							//横方向解像度，0でもよい
	LOAD_UI(avi_head[0x00c8], 0);							//縦方向解像度，0でもよい
	LOAD_UI(avi_head[0x00cc], 0);							//使用色（パレット）数（0でデフォルト）
	LOAD_UI(avi_head[0x00d0], 0);							//重要なパレットのインデックス（0でデフォルト）
	LOAD_UI(avi_head[0x00d4], fcc("JUNC"));				//リストチャンク
	LOAD_UI(avi_head[0x00d8], 0x10f4-0x00dc);			//リストサイズ
	LOAD_UI(avi_head[0x00dc], 4);
	LOAD_UI(avi_head[0x00e0], 0);
	LOAD_UI(avi_head[0x00e4], fcc("00dc"));
	LOAD_UI(avi_head[0x10f4], fcc("JUNC"));				//リストチャンク
	LOAD_UI(avi_head[0x10f8], 0x1622-0x10fc);			//リストサイズ
	LOAD_UI(avi_head[0x10fc], fcc("odml"));				//リストタイプ
	LOAD_UI(avi_head[0x1100], fcc("dmlh"));
	LOAD_UI(avi_head[0x1104], 0x1622-0x1108);
	LOAD_UI(avi_head[0x1622], fcc("LIST"));				//リストチャンク
//	LOAD_UI(avi_head[0x1626], 0);						//リストサイズ
	LOAD_UI(avi_head[0x162a], fcc("movi"));				//リストタイプ

	res = fatfs_write(&f_avi, avi_head, sizeof(avi_head), &size);
	if((FR_OK != res) || (sizeof(avi_head) != size))goto error_close;

	SYSTIM tim_start, tim_end, t = 0;
	get_tim(&tim_start);

	int n, button_off = 0;
	for(n = 0; ; n++)
	{
		irq_stop_m();

		if(!button_a_is_on() && !button_b_is_on())button_off = 1;
		if(button_off)
		{
			if(!hb_on)
			{
				if(button_a_is_on() || button_b_is_on())break;
			}
			else if(!button_hb_is_on())
			{
				hb_on = 0;
				break;
			}
		}

		int start_point = fatfs_tell(&f_avi);

		res = fatfs_write(&f_avi, "00dc", 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;

		res = fatfs_lseek(&f_avi, start_point + 8);
		if(FR_OK != res)goto error_close;

		char tmp[12+1];
		sprintf(tmp, "%02d:%02d:%02d", hour, min, sec);
		lcd_puts_s(0, 0, tmp, FCOLOR, BCOLOR);

		lcd_puts_s(9, 0, latitude, FCOLOR, BCOLOR);
		lcd_puts_s(19, 0, longitude, FCOLOR, BCOLOR);
		lcd_puts_s(30, 0, speed, FCOLOR, BCOLOR);

		int i;
		for(i = 0; i < 3; i++)
		{
			sprintf(tmp, "%c%d.%03d", ((sensor_g[i] < 0) ? '-' : ' '), abs(sensor_g[i]) / 1000, abs(sensor_g[i]) % 1000);
			lcd_puts_s(3 + i * 14, 1, tmp, FCOLOR, BCOLOR);
		}

		display_read_set_m(0, 0, 240 - 1, 320 - 1);

		lcd_wr_cmd(0x0020);lcd_wr_data(0x0000); // GRAM horizontal Address
		lcd_wr_cmd(0x0021);lcd_wr_data(0x0);//13F); // GRAM Vertical Address
		lcd_wr_cmd(0x0022);

		lcd_rd_data();
		lcd_rd_data();

		last_size = 0;
		write_JPEG_file(&f_avi, QUALITY);

		display_area_set_m(0, 0, 240 - 1, 320 - 1);

		if(last_size % 4)
		{
			res = fatfs_write(&f_avi, "\0\0\0\0", (4 - last_size % 4), &size);
		}
		if(FR_OK != res)goto error_close;

		res = fatfs_write(&f_siz, &last_size, 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;

		res = fatfs_sync(&f_siz);
		if(FR_OK != res)break;

		int end_point = fatfs_tell(&f_avi);
		res = fatfs_lseek(&f_avi, start_point + 4);
		if(FR_OK != res)goto error_close;

		last_size = (last_size + 3) & ~3;

		res = fatfs_write(&f_avi, &last_size, 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;

		res = fatfs_lseek(&f_avi, start_point + 8 + 6);
		if(FR_OK != res)goto error_close;

		res = fatfs_write(&f_avi, "AVI1", 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;

		res = fatfs_sync(&f_avi);
		if(FR_OK != res)break;

		res = fatfs_lseek(&f_avi, end_point);
		if(FR_OK != res)goto error_close;

		irq_start();
	}
	get_tim(&tim_end);
	t = (tim_end - tim_start) / n;

	int tmp;
	int fnum = n;
	res = fatfs_write(&f_avi, "idx1", 4, &size);
	if((FR_OK != res) || (4 != size))goto error_close;

	tmp = 0x10 * fnum;
	res = fatfs_write(&f_avi, &tmp, 4, &size);

	save_gps = 0;
	fatfs_close(&f_gps);

	int pos = 4, size_all = 0;
	fatfs_close(&f_siz);
	res = fatfs_open(&f_siz, f_name_siz, FA_READ);
	if(FR_OK != res)goto error_close;

	for(n = 0; n < fnum; n++)
	{
		res = fatfs_write(&f_avi, "00dc", 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;
		tmp = 0x12;
		res = fatfs_write(&f_avi, &tmp, 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;
		res = fatfs_read(&f_siz, &tmp, 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;
		size_all += tmp;
		res = fatfs_write(&f_avi, &pos, 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;
		tmp = (tmp + 3) & ~3;
		res = fatfs_write(&f_avi, &tmp, 4, &size);
		if((FR_OK != res) || (4 != size))goto error_close;

		pos += tmp + 8;
	}

	res = fatfs_lseek(&f_avi, 0x04);
	if(FR_OK != res)goto error_close;
	tmp = (sizeof(avi_head) - 8) + pos + 4 + 16 * fnum;
	res = fatfs_write(&f_avi, &tmp, 4, &size);
	if((FR_OK != res) || (4 != size))goto error_close;

	res = fatfs_lseek(&f_avi, 0x20);
	if(FR_OK != res)goto error_close;
	tmp = 1000 * t;
	res = fatfs_write(&f_avi, &tmp, 4, &size);
	if((FR_OK != res) || (4 != size))goto error_close;

	res = fatfs_lseek(&f_avi, 0x24);
	if(FR_OK != res)goto error_close;
	tmp = 1000000 * size_all / fnum / tmp;
	res = fatfs_write(&f_avi, &tmp, 4, &size);
	if((FR_OK != res) || (4 != size))goto error_close;

	res = fatfs_lseek(&f_avi, 0x30);
	if(FR_OK != res)goto error_close;
	res = fatfs_write(&f_avi, &fnum, 4, &size);
	if((FR_OK != res) || (4 != size))goto error_close;

	res = fatfs_lseek(&f_avi, 0x80);
	if(FR_OK != res)goto error_close;
	tmp = 1000 * t;
	res = fatfs_write(&f_avi, &tmp, 4, &size);
	if((FR_OK != res) || (4 != size))goto error_close;

	res = fatfs_lseek(&f_avi, 0x8c);
	if(FR_OK != res)goto error_close;
	res = fatfs_write(&f_avi, &fnum, 4, &size);
	if((FR_OK != res) || (4 != size))goto error_close;

	res = fatfs_lseek(&f_avi, 0x1626);
	if(FR_OK != res)goto error_close;
	res = fatfs_write(&f_avi, &pos, 4, &size);
	if((FR_OK != res) || (4 != size))goto error_close;

	fatfs_close(&f_avi);
	fatfs_unlink(f_name_siz);
	index_add(filenum++);
	lcd_puts(LCD_LEFT, 4, f_name_avi, FCOLOR, BCOLOR);
	beep_long();
	wait_busy(1000);
	irq_start();
	return;

error_close:
	fatfs_close(&f_avi);
	fatfs_close(&f_siz);
	save_gps = 0;
	fatfs_close(&f_gps);
	fatfs_unlink(f_name_siz);
error:
	lcd_puts(3, 2, "error", FCOLOR, BCOLOR);
	wait_busy(1000);
	return;
}
