
//
//
#include "pico/stdlib.h"
#include <stdio.h>
#include <string.h>
#include <pico/multicore.h>
#include <pico/sem.h>
#include "stdlib.h"
#include "hardware/vreg.h"
#include "hardware/clocks.h"
#include "math.h"
#include "hardware/adc.h"

#include "DVIinit_640x480x8_v1.h"
#include "pt_cornell_rp2040_v1_4.h"

float core_temp ;

// ==================================================
// === dvi test thread 
// ==================================================
//   
static PT_THREAD (protothread_dvi(struct pt *pt))
{
    // every thread begins with PT_BEGIN(pt);
    PT_BEGIN(pt);
    // animation state variables
    static int x_pos = 50, x_dir=1 ;
    static float x_pos1 = 50.0f, x_dir1=-0.25f ;
    static int x_pos2 = 50, x_dir2=1 ;
    static int k ;
    static int frame_count ;
    static char round_color, round_color1 ;
    //
    // Title block -- put static stuff at top
      fillRect(0, 0, 639, 39,rgb332(1,1,1));
      fillRect(0, 39, 639, 2, 0xff);
      drawTextTiny8(2, 2, "Cornell ECE4760", 0xff, rgb332(1,1,1));
      drawTextTiny8(2, 12, "Pico2 rp2350", 0xff, rgb332(1,1,1));
      drawTextTiny8(2, 22, "Hunter Adams", 0xff, rgb332(1,1,1));
      drawTextArial24(180, 2, "DVI 640x480 8-bit", 0xff, rgb332(1,1,1));
      drawTextTiny8(550, 22, "Bruce Land", 0xff, rgb332(1,1,1));
      drawTextTiny8(550, 12, "04nov2026", 0xff, rgb332(1,1,1));
      drawTextTiny8(550, 1, "DVI Ver 1", 0xff, rgb332(1,1,1));
      char str[20]; 
      sprintf(str, "clk_sys=%3u MHz", sys_clock_actual/1000000);
      drawTextVGA437 (180, 22, str, 0xff, rgb332(1,1,1));
      // end title block
      //

      while(1) {
        //
        // wait here for frame start
        PT_YIELD_UNTIL(pt, start_frame==1);
        gpio_put(2,1);
        start_frame = false;

        // clear buffer
        // above 40 is static image
        clearRegion(40,479, rgb332(1,1,1)) ;
        frame_count++ ;
        //
        // animation control for three different trajectories
        if (x_pos2>50) x_dir2 = -2 ;
        else if (x_pos2<2) x_dir2 = 2;
        x_pos2 += x_dir2 ;
        //
        if (x_pos>200) x_dir = -1 ;
        else if (x_pos<5) x_dir = 1;
        x_pos += x_dir ;
        //
        if (x_pos1>80.0f) x_dir1 = -0.25f ;
        else if (x_pos1<5.0f) x_dir1 = 0.25f;
        x_pos1 += x_dir1 ;        
        //

        // static color pallet samples
      #define sq_wide 30
      #define sq_high 20
      #define top_start 45
      
      if (frame_count % 120 == 0) {
        k = (k+1) % 4 ;
      }
      for(int j=0; j<8; j++){
          for(int i=0; i<8; i++){
              fillRect(i*sq_wide, j*sq_high+top_start, 
                  sq_wide-1, sq_high-1, rgb332(i,j,k));
              sprintf(str, "%02x", rgb332(i,j,k));
              drawTextTiny8(i*sq_wide+1, j*sq_high+top_start+1, str, 
                (i+j+k<4)? 0xff : 0,  rgb332(i,j,k));
          }
      }

      // grey scale
        fillRect(320,45,20, 20,rgb332(7,7,3));
        fillRect(340,45,20, 20,rgb332(6,6,3));
        fillRect(360,45,20, 20,rgb332(5,5,2));
        fillRect(380,45,20, 20,rgb332(3,3,1));
        fillRect(400,45,20, 20,rgb332(2,2,1));
        fillRect(420,45,20, 20,rgb332(1,1,0));
        //drawTextTiny8(444, 45, "Grey Scale", 0xff, rgb332(1,1,1));
        //
      
        drawRect(x_pos, 210, 40, 35, rgb332(5,5,2));
        fillRect(x_pos, 250, 40, 35, rgb332(5,5,2));
        //
        drawCircle(100, 320, (int)x_pos1+2, rgb332(7,0,7));
        fillCircle(100, 320, (int)x_pos1, rgb332(7,0,0));
        //
        fillTri(500, 122+x_pos, 550, 130+x_pos, 525, 200+x_pos, rgb332(0, 3, 0)) ;
        short point_list[4][2] = {500, 132, 550, 140, 525, 210, 500, 132} ;
        drawMultiLine(4, point_list, rgb332(0,7,0)) ;

        if (frame_count%250 == 0){
          round_color = rgb332(rand()&7, rand()&7,rand()&3);
          round_color1 = rgb332(rand()&7, rand()&7,rand()&3);
        }
        sprintf(str, "outer color = 0x%02x", round_color );
        drawTextVGA437(300, 212, str, 0Xff, rgb332(1,1,1));
        //
        sprintf(str, "inner color = 0x%02x", round_color1 );
        drawTextVGA437(300, 332, str, 0Xff, rgb332(1,1,1));
       //
        fillRoundRect(300, 100, 150, 100, 20, round_color);
        sprintf(str, "0xff on 0x%02x", round_color );
        drawTextVGA437(330, 110, str, 0XFF, round_color); 
        sprintf(str, "0x00 on 0x%02x", round_color ); 
        drawTextVGA437(330, 130, str, 0X00, round_color);
        sprintf(str, "0xfc on 0x%02x", round_color ); 
        drawTextVGA437(330, 150, str, 0xfc, round_color);
        sprintf(str, "~0x%02x on 0x%02x", (char)~round_color, round_color ); 
        drawTextVGA437(330, 170, str, ~round_color, round_color);
        //
        fillRoundRect(300, 230, 150, 100, 20, round_color);
        fillRoundRect(325, 250, 100, 60, 20, round_color1);
        
        // samples of the fonts
        drawTextTiny8(20, 360, "Tiny 8 font - Dense and fast to draw", 0XFF, rgb332(1,1,1));
        drawTextVGA437(20, 372, "Block 437 font- Classic IBM font from 1982 - fast to draw", 
                      0XFF, rgb332(1,1,1));
        drawTextBigFont_16x16(20, 385, "Blocky 16x16 font - bold, low density", 0XFF, rgb332(1,1,1));
        drawTextArialBold_16x16(20, 403, "Arial 16x16 font - lower density ", 0XFF, rgb332(1,1,1));
        drawTextArial24(20, 420, "Arial 16x24 font - clean, medium font", 0XFF, rgb332(1,1,1));       
        drawTextGrotesk32(20, 443, "Grotesk32 font - bigger titles", 0XFF, rgb332(1,1,1));
        //
        drawTextTiny8(444, 55, "Grey Scale", 0xff, rgb332(1,1,1));

        // fill test 2000 circles is about the limit
        // above that get flicker or nothing
        // prazctical limit is ~500
        // for (int i=0; i<500; i++){
        //   fillCircle(i, 240, 5, 0xff);
        // }
        
        // line and text maek sure we are drawing whole screen
        sprintf(str,"core_temp=%4.1f C", core_temp) ;
        drawTextVGA437(328, 22, str, 0xff, rgb332(1,1,1));
        //
        drawHLine(200, 479, 200, 0xff); 
        gpio_put(2,0);
        // NEVER exit WHILE in a thread
      } // END WHILE(1)
      // every thread ends with PT_END(pt);
      PT_END(pt);
} // end blink thread

// ==================================================
// ADC read thread protothread
// ==================================================
// this currently reads the internal temperature
// and populates a circuilar buffer 100 long
static PT_THREAD (protothread_temp(struct pt *pt)){
    PT_BEGIN(pt);
    
    
    const float conversion_factor = 3.3f / 4096.0f ;

    adc_init();
    adc_set_temp_sensor_enabled(true);
    adc_select_input(4);

    while(true) {  
        PT_YIELD_usec(1000000);
        // conver to volts
        float result = (float)adc_read() * conversion_factor;
        //printf("adc %d v %f \n", adc_read(), result);
        /// convert volts to ~temp (from hardare manual)
        core_temp = 27.0f - (result -0.706f)/0.001721f ;
    }   
    PT_END(pt);
}
// ========================================
// === core 1 main -- started in main below
// ========================================
void core1_main(){ 
    //  === add threads  ====================
    // for core 1
    pt_add_thread(protothread_temp);
    //
    // === initalize the scheduler ==========
    pt_sched_method = SCHED_ROUND_ROBIN ;
    pt_schedule_start ;
    // NEVER exits
    // ======================================
  } 

// ========================================
// === core 0 main
// ========================================
// VREG_VOLTAGE_1_30 = 0b01111,    // 300 MHz
// #if !PICO_RP2040
//     // Above this point you will need to set POWMAN_VREG_CTRL_DISABLE_VOLTAGE_LIMIT
//     VREG_VOLTAGE_1_35 = 0b10000,
//     VREG_VOLTAGE_1_40 = 0b10001, // 450 MHz
//     VREG_VOLTAGE_1_50 = 0b10010,

int main(){
  // set the core voltage only necessarey for 300 MHz 
  vreg_disable_voltage_limit ();
  vreg_set_voltage(VREG_VOLTAGE_1_30 );
  // set the clock 250 works
  set_sys_clock_khz (300000, false); //375
  // stablize clock
  sleep_ms(10);
  // record clock for HSTX setup
  sys_clock_actual = clock_get_hz(clk_sys) ;

  //===  start the serial i/o ==================
  stdio_init_all() ;
  // announce the threader version on system reset
  // if there is a seral terminal attached
  printf("\n\rProtothreads RP2040 v1.4 two-core, priority\n\r");

  // fire up the HSTX
  DVIinit() ;

  // JUIST FOR TIMING
  gpio_init(2) ;	
  gpio_set_dir(2, GPIO_OUT) ;

  // start core 1 threads
  multicore_reset_core1();
  multicore_launch_core1(&core1_main);

  // === config threads ========================
  // for core 0 two threads
  pt_add_thread(protothread_dvi);
  //pt_add_thread(protothread_serial);
  
  // === initalize the scheduler ===============
  // method is either:
  //   SCHED_PRIORITY or SCHED_ROUND_ROBIN
  pt_sched_method = SCHED_ROUND_ROBIN ;
  pt_schedule_start ;
  // !!pt_schedule_start NEVER exits
  // ===========================================
} // end main
///////////
// end ////
///////////
