/* ----------------------------------------------------------------------------
*         ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2012, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/

/**
 *  \page Continuous_Capture Continuous Capture Demo
 *
 *  \section Purpose
 *
 *  This demo is for evaluating the performance when capturing and displaying
 *  are always on. Both monochrome mode and color mode are provided.
 *
 *  \section Requirements
 *
 *  This package can only be used with sam4s-wpir-rd.
 *
 *  \section Description
 *
 *  The demo continously captures from image sensor and displays to LCD if
 *  mounted.
 *
 *  BP1 is used to switch the mode between black/white and color mode.
 *  Each time switching from one mode to another, the system will be re-
configured
 *  to support different output which mainly includes half sampling and color
 *  conversion from YUV to RGB for displaying.
 *
 *  \section Usage
 *
 *  -# Build the program and download it inside the evaluation board. Please
 *     refer to the
 *     <a href="http://www.atmel.com/dyn/resources/prod_documents/doc6224.pdf">
 *     SAM-BA User Guide</a>,
 *     application note or to the
 *     <a href="ftp://ftp.iar.se/WWWfiles/arm/Guides/EWARM_UserGuide.ENU.pdf">
 *     IAR EWARM User Guide</a>,
 *     depending on your chosen solution.
 *  -# After downloading through SAM-BA or IAR flash loader, run the 
application.
 *  -# Press BP1 to switch between b/w and clor mode.
 */

/** \file
 *
 *  This file contains all the specific code for the Continuous_Capture.
 */

/*----------------------------------------------------------------------------
*        Headers
*----------------------------------------------------------------------------*/

#include "board.h"

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "cv.h"
#include "imagereader.h"
#include "decodeqr.h"
#include "barcode.h"
/*----------------------------------------------------------------------------
*        Local definitions
*----------------------------------------------------------------------------*/

/** lcd debug output support, uncomment to remove */
#define LCD_DEBUG_OUT

/*macro to measure Frame rate, comment if it's not used*/
#define FRAME_RATE_MEASURE

#define IMAGE_WIDTH     (320)
#define IMAGE_HEIGHT    (240)
#define SCAN_LINES	     80
/** the captured data length per line in different color mode,double the size
in YUV full output*/
#define IMAGE_LINE_COLORED    (IMAGE_WIDTH * 2)
#define IMAGE_LINE_MONO  (IMAGE_WIDTH)

#define PIO_PCMR_DSIZE_8 (0x0<<4)
#define PIO_PCMR_DSIZE_16 (0x1<<4)
#define PIO_PCMR_DSIZE_32 (0x2<<4)

#define SRAM2_BASE (0x60200000)
#define CAP_DEST  (SRAM2_BASE)

/** TWI clock frequency in Hz (400KHz) */
#define TWCK            100000

/** for power on or off*/
#define ON  true
#define OFF false

/** The output formats supported by the sensor*/
typedef enum {
    OUT_RGB,
    OUT_YUV
}OUT_FMT;

/** the default output mode is YUV422*/
#define DEFAULT_SENSOR_YUV422

/** uncomment for monochrome output*/
#define DEFAULT_MODE_COLORED

/*----------------------------------------------------------------------------
*        Local variables
*----------------------------------------------------------------------------*/
/** the output format of image sensor*/
OUT_FMT out_format = OUT_YUV;
/** the output color mode of image sensor*/
volatile PIO_OUT_MODE current_mode = PIO_OUT_COLOR;
volatile PIO_OUT_MODE next_mode = PIO_OUT_COLOR;

/** is displaying needed*/
bool isDisplayed = true;
/** is Captured already*/
bool isTriggered = false;
/** capturing destination buffer*/
uint8_t *cap_dest_buf;

/** displaying destination buffer, RESERVED*/
uint8_t *disp_dest_buf;

/** cpaturing rows*/
uint16_t cap_rows = IMAGE_HEIGHT;

/** cap line length*/
uint16_t cap_line = IMAGE_LINE_COLORED;

/** VSYNC flag*/
volatile bool vsync_flag = false;

/** HSYNC flag RESERVED*/
bool hsync_flag = false;

/** TWI driver*/
static Twid twid;

/** BP1*/
const Pin pinBP1 = PIN_PUSHBUTTON_1;


/*const Pin pinHSYNC = PIN_OV_HSYNC;*/

/** vsync pin*/
const Pin pinVSYNC = PIN_OV_VSYNC;

/*----------------------------------------------------------------------------
*        Local functions
*----------------------------------------------------------------------------*/
/**
 * \brief Set default master access for speed up.
 * Here assume code is put at flash, data is put at sram.
 * The PDC transfer target at SRAM
 */
static void _SetDefaultMaster( void )
{
    Matrix *pMatrix = MATRIX;

    /* Set default master: SRAM (slave 0)-> Cortex-M4 System (Master 1)*/
    pMatrix->MATRIX_SCFG[0] |= ((1 << 18) & MATRIX_SCFG_FIXED_DEFMSTR_Msk) | 
/* Master 1 */
                               ((2 << 16) & MATRIX_SCFG_DEFMSTR_TYPE_Msk);   
/* Fixed Default Master */

    /* Set default master: Internal flash (slave 2) -> Cortex-M4 Instruction/
Data (Master 0)*/
    pMatrix->MATRIX_SCFG[2] |= ((0 << 18) & MATRIX_SCFG_FIXED_DEFMSTR_Msk) | 
/* Master 0 */
                               ((2 << 16) & MATRIX_SCFG_DEFMSTR_TYPE_Msk);   
/* Fixed Default Master */

    /* Set default master: EBI (slave 3) -> PDC (Master 2)*/
    pMatrix->MATRIX_SCFG[3] |= ((2 << 18) & MATRIX_SCFG_FIXED_DEFMSTR_Msk) | 
/* Master 2 */
                               ((2 << 16) & MATRIX_SCFG_DEFMSTR_TYPE_Msk);   
/* Fixed Default Master */
}

/** reconfigure PIO for color or mono mode*/
static void _Capture_Reconfigure(void)
{
    PIO_Capture_Switch(PIOA,OFF);

    /* initialize PIODC*/
    PIODC_Capture_Init(PIOA, ID_PIOA, current_mode);

    /** intialize capturing line*/
    if(current_mode == PIO_OUT_COLOR)
    {
        cap_line = IMAGE_LINE_COLORED;
    }
    else if(current_mode == PIO_OUT_MONO)
    {
        cap_line = IMAGE_LINE_MONO;
    }
    else
    {
        cap_line = IMAGE_LINE_COLORED;
    }

    /* reset PDC channel*/
    PIO_RX_Disable(PIOA);
    PIO_RCR_Set(PIOA, 0);
    PIO_RNCR_Set(PIOA, 0);
}

/** entry for capturing*/
static void _DoCapture(uint8_t height)
{
    uint8_t *buf;

    /* set capturing destination address*/
    cap_dest_buf = (uint8_t*)CAP_DEST;

    buf = cap_dest_buf;

    cap_rows = height;

    /* enable vsync interrupt*/
    PIO_EnableIt(&pinVSYNC);

    /* sync with flag*/
    while(!vsync_flag);

    /* disable first*/
    PIO_DisableIt(&pinVSYNC);

    PIO_Capture_Switch(PIOA,ON);

    /* only using Vsync*/
    PIO_CaptureToBuffer(PIOA,buf, (cap_line * cap_rows)>>2);

    while(!PIO_Capture_BUFF(PIOA));

    PIO_Capture_Switch(PIOA,OFF);

    /* clear vsync flag*/
    vsync_flag = false;
}

/** intialize LCD for debugging output*/
static void _Init_LCD(void)
{
    /* Initialize LCD */
    LCDD_Initialize();
    LCD_On();
}

static void _DrawFrame_YUV_BW8( uint8_t* pucData )
{
    volatile uint32_t dwCursor ;
    LCD_SetDisplayLandscape(0);
    LCD_SetWindow(0,0,IMAGE_HEIGHT,IMAGE_WIDTH);
    LCD_SetCursor(0,0) ;
    LCD_WriteRAM_Prepare() ;
    for ( dwCursor=IMAGE_WIDTH*IMAGE_HEIGHT ; dwCursor != 0 ; dwCursor-- ,
pucData++)
    {
        // Black and White using Y
        LCD_WriteRAMByte( *pucData ) ;
        LCD_WriteRAMByte( *pucData ) ;
        LCD_WriteRAMByte( *pucData ) ;

    }
}

static void _DrawFrame_YUV_BW8_BAR( uint8_t* pucData )
{
    volatile uint32_t dwCursor ;
    LCD_SetDisplayLandscape(0);
    LCD_SetWindow(80,0,SCAN_LINES,IMAGE_WIDTH);
    LCD_SetCursor(80,0) ;
    LCD_WriteRAM_Prepare() ;
    for ( dwCursor=IMAGE_WIDTH*SCAN_LINES ; dwCursor != 0 ; dwCursor-- ,
pucData++)
    {
        // Black and White using Y
        LCD_WriteRAMByte( *pucData ) ;
        LCD_WriteRAMByte( *pucData ) ;
        LCD_WriteRAMByte( *pucData ) ;

    }
}
/** turn on or off image sensor power*/
static void _ImageSensor_Switch(bool on)
{
    Pin pinsPower[]={ PINS_OV };
    PIO_Configure(pinsPower,PIO_LISTSIZE(pinsPower));

    if(on)
    {
        PIO_Clear(&pinsPower[0]);
    }
    else
    {
        PIO_Set(&pinsPower[0]);
    }
}

/** Initialize Image Sensor*/
static void _Init_Image_Sensor(const EOV7740_Format eFormat)
{
    static bool bInitialized = false;
    /* backup setting for future use*/
    static uint32_t bk_setting[2]={0};

    /* intitializ Master Clock to xvclk1*/
    Pin pinPCK = PIN_PCK0;
    const Pin pinsTWI[] = {PINS_TWI0};
    Pmc *pmc = (Pmc*) PMC;

    /* Power on*/
    _ImageSensor_Switch(ON);

    /* configure Master Clock*/
    PIO_Configure(&pinPCK,1);

    /* PLLA is 96MHz so that PCK0 is 96MHz/4 = 24MHz*/
    pmc->PMC_PCK[0] = (PMC_PCK_PRES_DIV4 | PMC_PCK_CSS_PLLACLOCK);

    pmc->PMC_SCER = PMC_SCER_PCK0;

    while(!( pmc->PMC_SCSR & PMC_SCSR_PCK0));

    /* twi*/
    PIO_Configure(pinsTWI,PIO_LISTSIZE(pinsTWI));

    PMC_EnablePeripheral(ID_TWI0);

    TWI_ConfigureMaster(TWI0, TWCK, BOARD_MCK);

    TWID_Initialize(&twid, TWI0);

    /* Configure TWI interrupts */
    NVIC_DisableIRQ(TWI0_IRQn);
    NVIC_ClearPendingIRQ(TWI0_IRQn);
    NVIC_SetPriority(TWI0_IRQn, 0);
    NVIC_EnableIRQ(TWI0_IRQn);

    while( ov_init(&twid) == 0 )
    {
        printf("-I- Retry init\n\r");
    }
    printf("-I- Init passed\n\r");

    /* OV7740 configuration */
    ov_configure(&twid, eFormat);

    /* first time*/
    if(!bInitialized)
    {
        Wait(3000);
        ov_sotre_manual(&twid,bk_setting,2);
        bInitialized = true;
    }
    else
    {
        ov_resotre_manual(bk_setting,2);
        ov_configure_manual(&twid);
    }
}

/** interrupt handler for vsync*/
static void _Vsync_Handler(const Pin *pin)
{
    vsync_flag = true;
}

/** intialize fram marker signal response*/
static void _Init_HVSync_Interrupts(void)
{
    PIO_Configure(&pinVSYNC,1);

    PIO_ConfigureIt(&pinVSYNC, _Vsync_Handler);

    /* Enable PIO controller IRQs. */
    NVIC_EnableIRQ(PIOA_IRQn);
}

/** button1 interrupt handler, handle different color mode*/
static void _Button1_Handler(const Pin *pin)
{
  isTriggered = !isTriggered;
}

/** initialize pushbutton for state transition*/
static void _Init_Pushbutton_Trigger(void)
{
    /* Configure pios as inputs. */
    PIO_Configure( &pinBP1, 1 ) ;

    /* Adjust pio debounce filter patameters, uses 10 Hz filter. */
    PIO_SetDebounceFilter( &pinBP1, 10 ) ;

    /* Initialize pios interrupt handlers, see PIO definition in board.h.
           Interrupt on rising edge
       */
    PIO_ConfigureIt( &pinBP1, _Button1_Handler ) ;

    /* Enable PIO controller IRQs. */
    NVIC_EnableIRQ( (IRQn_Type)pinBP1.id ) ;

    /* Enable PIO line interrupts. */
    PIO_EnableIt( &pinBP1 ) ;
}


static void _Capture_Init(void)
{
    /* OUTPUT color mode*/
#ifdef DEFAULT_MODE_COLORED
    current_mode = PIO_OUT_COLOR;
#else
    current_mode = PIO_OUT_MONO;
#endif
    PIO_InitializeInterrupts(0);

    /* configure SRAM*/
    BOARD_ConfigureSRAM(SMC);

    /* initizale push botton for capturing trigger*/
    _Init_Pushbutton_Trigger();

    /* intialize Frame signal*/
    _Init_HVSync_Interrupts();

    /* initialize PIODC*/
    PIODC_Capture_Init(PIOA, ID_PIOA, current_mode);

    /** intialize capturing line*/
    if(current_mode == PIO_OUT_COLOR)
    {
        cap_line = IMAGE_LINE_COLORED;
    }
    else if(current_mode == PIO_OUT_MONO)
    {
        cap_line = IMAGE_LINE_MONO;
    }
    else
    {
        cap_line = IMAGE_LINE_COLORED;
    }

#ifdef DEFAULT_SENSOR_YUV422
    _Init_Image_Sensor(QVGA_YUV422);
#endif
}

/*----------------------------------------------------------------------------
*        Exported functions
*----------------------------------------------------------------------------*/

/** system systick handler*/
void SysTick_Handler( void )
{
    TimeTick_Increment() ;
}

/**
*  \brief Continuous_Capture Application entry point.
*
*  Initialize adc to 12bit,enable channel 15,turn on
*  temp sensor, pdc channel interrupt for temp sensor
*  and start conversion
*
*  \return Unused (ANSI-C compatibility).
*  \callgraph
*/
extern int main( void )
{

int 	  show_bin_image = 1;
IplImage  *camera;
CvSize    size;
int       type,flag,sz,bar;

/* Disable watchdkog */
WDT_Disable( WDT ) ;
/* Set default masters to promote speed*/
_SetDefaultMaster();

/* initialize 1ms systick*/
TimeTick_Configure(BOARD_MCK);
Wait(100); // wait for 1s to be sure LCD is ready for initialization    
/* LCD for display*/
_Init_LCD();
LCDD_Fill(COLOR_TURQUOISE);
LCDD_DrawString( 0, 10, "BAR Code and", COLOR_BLACK );
LCDD_DrawString( 0, 30, "QR Scanner Demo", COLOR_BLACK );
LCDD_DrawString( 0, 50, "With External SRAM", COLOR_BLACK );
LCDD_DrawString( 0, 120, "Switch Modes by pressing button", COLOR_BLACK);
Wait(2000);    
/* initialize image sensor and PIO parallel capture*/
_Capture_Init();
  bar = 1;
while(1){
 if(isTriggered){
  size.height = IMAGE_HEIGHT;
  size.width  = IMAGE_WIDTH;
  type = IPL_DEPTH_8U;   
  camera = cvCreateImageHeader(size,type,1);
  camera->imageData = camera->imageDataOrigin = (char*)cap_dest_buf;
  IplImage *bin=NULL;
  current_mode = PIO_OUT_MONO;
  _Capture_Reconfigure();
  _DoCapture(IMAGE_HEIGHT);
  QrDecoderHandle decoder=qr_decoder_open();
  int status =-1;
  if(camera){
    qr_decoder_set_image_buffer(decoder,camera);
    }
   else
    status = 1;        
  unsigned char *text=NULL;
  int text_size=0;
  sz = 25;
  while((status<=0)&&isTriggered){
        // display each frame in bin mode
      	disp_dest_buf = cap_dest_buf; 
        _DrawFrame_YUV_BW8(disp_dest_buf);
  	if(!qr_decoder_is_busy(decoder)){      
      	    status = -1;
        // Then QR code detect
	   flag = qr_decoder_detect(decoder);
	// Show the detect result for QR
	   if ((flag & QR_IMAGEREADER_NOT_FOUND_FINDER_PATTERN) ==0){
	      _Init_LCD();
              LCDD_Fill(COLOR_TURQUOISE);
	      LCDD_DrawString(0,10,"QR Pattern Found",COLOR_BLACK);
	      LCDD_DrawString(0,40,"Decoding...",COLOR_BLACK);
              // Decode processing
              for(sz=25,flag=0;
                (sz>=19)&&((flag&QR_IMAGEREADER_DECODED)==0);
                sz-=2)
                flag= qr_decoder_decode(decoder,sz);
              printf("Adaptive %d, flag is 0x%X\n\r",sz,flag);
              // for debug, show binarized image. could be enabled by show_bin_image = 1;
              if(bin)
                 cvReleaseImage(&bin);
              if(show_bin_image){
                 bin=cvCloneImage(qr_decoder_get_binarized_image_buffer(decoder));
                 disp_dest_buf = (uint8_t*)bin->imageData; 
                 _DrawFrame_YUV_BW8(disp_dest_buf);
                 Wait(1000);
                 }
               // once QR sucessefully decoding, print decoded text.
              QrCodeHeader header;
    	      if((flag == 0x2000) || (flag == 0x2008)){
            	if( qr_decoder_get_header(decoder,&header)){
                  if(text_size<header.byte_size+1){
                    if(text)
                       delete text;
                    text_size=header.byte_size+1;
                    text=new unsigned char[text_size];
                    }
                  qr_decoder_get_body(decoder,text,text_size);
                  printf("QR Scanner Result:%s\n\r",text);	    	
                  _Init_LCD();
           	  LCDD_Fill(COLOR_TURQUOISE);
                  LCDD_DrawString(0,10,"QR Scanner Result:",COLOR_BLACK);
                  LCDD_DrawString(0,50,(uint8_t *)text,COLOR_BLACK); 
                  Wait(3000);
    		  status = 1;
	     	  }
	         }
	// once QR decode failed
                else{
                    _Init_LCD();
        	    LCDD_Fill(COLOR_TURQUOISE);
                    printf("ERR Code %#0*X",8, flag);
		    if (flag&QR_IMAGEREADER_ERROR)
			LCDD_DrawString(0,30,"Invalid QR",COLOR_BLACK);
		    if (flag&QR_VERSIONINFO_ERROR)
			LCDD_DrawString(0,50,"Invalid QR Version",COLOR_BLACK);
		    if (flag&QR_FORMATINFO_ERROR)
        		LCDD_DrawString(0,70,"Invalid QR Format",COLOR_BLACK);
        	    if (flag&QR_CODEDATA_ERROR)
        		LCDD_DrawString(0,90,"Invalid QR Code Data",COLOR_BLACK);
        	   Wait(1000);
    		   status = 1;
        	   }
      		  }
      	       }
                current_mode = PIO_OUT_MONO;
                _Capture_Reconfigure();
	        _DoCapture(IMAGE_HEIGHT);
       }

  if(text)
  	delete text;
  qr_decoder_close(decoder);
  if(bin)
     cvReleaseImage(&bin);
  if(camera)
     cvReleaseImageHeader(&camera);
  camera = NULL;
  bar =1;
  }
  else{ 
      LCDD_Fill(COLOR_BLACK);
    while(!isTriggered)
    {
       current_mode = PIO_OUT_MONO;
       _Capture_Reconfigure();
      _DoCapture(SCAN_LINES);
      disp_dest_buf = cap_dest_buf;
      LCD_SetColor(COLOR_YELLOWGREEN);     
      LCDD_DrawRectangleWithFill(0,0,20,20,COLOR_WHITE);
      LCDD_DrawString(0, 30, "BAR",COLOR_YELLOWGREEN);
      LCDD_DrawString(0, 60, "SCANER",COLOR_YELLOWGREEN);
      LCDD_DrawString(0, 90, "DEMO",COLOR_YELLOWGREEN);
      LCDD_DrawString(0, 200, "PRESS",COLOR_YELLOWGREEN);
      LCDD_DrawString(0, 230, "BUTTON",COLOR_YELLOWGREEN);
      LCDD_DrawString(0, 260, "TO",COLOR_YELLOWGREEN);
      LCDD_DrawString(0, 290, "QRMODE",COLOR_YELLOWGREEN);
      _DrawFrame_YUV_BW8_BAR(disp_dest_buf);
      bar = scan_bar_code(1); 
    }
  }
  }
} 

