/**************************************************************************
Name            - main()
Parameters      - Nothing
Responsibility  - Main control structure for the AZ/EL rotor control system
                
Notes           - Most recent development environment used:
                    OS  Linux Mint - Mate edition
                    avr-gcc Compiler version 1:4.7.2-2
                    avr-libc version 1:1.8.0-3
                    avrdude version 5.11.1.1
                    Last updated Jan. 2014

                - Controller -- Atmel ATmega32 with 8.0 Mhz external resonator
                                Use with different resonator freq. will require
                                code changes.

                                This project was started in 2006.
                                The ATmega32 part was current at that time.
                                At this time I would migrate to the ATmega 644
                                part for any significant enhancements.
                                However, be aware that this will require source code
                                changes since some of the on-board peripheral
                                control is not the same.
                                Fuse bits, etc. will also need to be re-evaluated.

                - FUSE BITS REQUIRED:
                    HFUSE = D9
                    LFUSE = FF

                 If using a parallel port cable:
		            parport0 may need to have its mode changed
                    before using avrdude in terminal mode.
                         chmod 0666 /dev/parport0

                    Verify fuse bits with:
                    avrdude -c dt006 -p m32 -P /dev/parport0 -t

                                   OR
                    avrdude -c usbtiny -p m32 -t               

                    I use usbtiny for all projects now so the Makefile
                    has been modified to reflect this.

                        read hfuse
                        read lfuse

                        write hfuse 00 0xd9
                        write lfuse 00 0xff

                        quit    - to exit avrdude in terminal mode

                 
Returns         - Nothing
Author          - Bill (N9CX)
**************************************************************************/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>


#include "uart_putchar.h"
#include "gpr.h"
#include "lcd.h"
#include "switches.h"
#include "sw_timers.h"
#include "uart_getchar.h"
#include "rotor.h"
#define LOCAL
#include "main.h"

void init_uart(void );
int out_queue(void);
void initialize(void);
void mode_priority(void);
void save_el_rotor(void);
void save_az_rotor(void);
void manual(struct rotor *);
void rotor_pwr(struct rotor *, char);
void calibrate(struct rotor *);
void init_timer1(void);
void init_rotor_ctl_io(void);
void rotor_bearing(struct rotor *);
void auto_track(struct rotor *);
void detect_rotor(struct rotor *);
char novacomm1_parser(char *cptr, int);
char Mac_Doppler_parser(char *cptr, int);
char parse_tracking_data(void);

int lcd_putc(char c, FILE * stream);
/*
This is one of the  new avr-libc options for stdio I/O
It opens the file structures and returns a pointer to it.
This happens prior to main being called.
*/
int uart_putchar(char c, FILE * stream);
int uart_getchar(char c, FILE * stream);

FILE uart_out = FDEV_SETUP_STREAM(uart_putchar,
                uart_getchar,
                _FDEV_SETUP_RW);

FILE lcd_out =  FDEV_SETUP_STREAM(lcd_putc,
                NULL,
                _FDEV_SETUP_WRITE);

char rotor_position(struct rotor *);
void update_display(void);

extern char new_auto_bearing;

int main(void) {

    mode = INITIALIZE;

    while(1) {
        switch( mode ) {
          case DETECT:
                detect_rotor(&az_rotor);
                detect_rotor(&el_rotor);
                if(az_rotor.detect_submode == DONE &&
                   el_rotor.detect_submode == DONE){
                        az_rotor.detect_submode = START;
                        el_rotor.detect_submode = START;
                        request_lcd_refresh = TRUE;
                        mode = MANUAL;
                }
              break;
            case MANUAL:
                if(az_rotor.type == 'P') manual(&az_rotor);
                if(el_rotor.type == 'P') manual(&el_rotor);
                break;
            case AUTO:
                    // "new_auto_bearing" is set by background interrupt sw.
                    if(new_auto_bearing){
                        new_auto_bearing = FALSE;
                        if(parse_tracking_data() ){
                           request_lcd_refresh = TRUE;
                        }
                    }
                if(az_rotor.type == 'P') auto_track(&az_rotor);

                if(el_rotor.type == 'P') auto_track(&el_rotor);

                break;
            case CALIBRATE:
                if(az_rotor.type == 'P') calibrate(&az_rotor);
                if(el_rotor.type == 'P') calibrate(&el_rotor);

                if( (el_rotor.cal_submode == FINISHED||el_rotor.type!='P') &&
                   (az_rotor.cal_submode == FINISHED|| az_rotor.type!='P') ){
                    if(el_rotor.type =='P') save_el_rotor();
                    if(az_rotor.type =='P') save_az_rotor();
                    request_lcd_refresh = TRUE;
                    mode = MANUAL;
                }
                break;
            case IDLE:
                break;
            case INITIALIZE: //Blocks till finished
                initialize();
                sei(); // enable global interrupts

                if(el_rotor.type != 'P' || az_rotor.type != 'P'){
                    mode = DETECT;
                }
                else {
                    mode = MANUAL;
                }
                request_lcd_refresh = TRUE;
                break;
            default:
               mode = MANUAL;
               break;
        }
        if(mode == MANUAL || mode == AUTO || mode == DETECT){
            if(rotor_position(&az_rotor)){
                rotor_bearing(&az_rotor);
                request_lcd_refresh = TRUE;
            }
            if(rotor_position(&el_rotor)){
                rotor_bearing(&el_rotor);
                request_lcd_refresh = TRUE;
            }
        }
        if(request_lcd_refresh){
            update_display();
            request_lcd_refresh = FALSE;
        }
        // Check for time out of the azimuth pulse limit timer
        if(!az_rotor.pulse_timer){
            if(az_rotor.moving){
                if(az_rotor.cw) az_rotor.limit = CW; // TO in CW direction
                else az_rotor.limit = CCW;           // TO in CCW direction
                az_rotor.pwr_ctl(&az_rotor, PWR_OFF);
            }
            else{
                // not moving so reset the timeout
                az_rotor.pulse_timer = PULSE_TIMEOUT;
            }
        }
        // Check for time out of the elevation pulse limit timer
        if(!el_rotor.pulse_timer){
            if(el_rotor.moving){
                if(el_rotor.cw) el_rotor.limit = CW; // TO in CW direction
                else el_rotor.limit = CCW;           // TO in CCW direction
                el_rotor.pwr_ctl(&el_rotor, PWR_OFF);
            }
            else{
                // not moving so reset the timeout
                el_rotor.pulse_timer = PULSE_TIMEOUT;
            }
        }
        // This can change the current "mode"
        mode_priority();
#ifdef DEBUG
        /*
        TAKE CARE - this is a development debug feature and
        will block if not used properly!
        Requires a splitter cable and a connected terminal.
        If you do not understand how to use this - don't define DEBUG.
        */
        if(mode != old_mode ) {
            fprintf(&uart_out, "%2.d ", mode);
            old_mode = mode;
        }
#endif

    
    }
}
