#include <Arduino.h>
#include <string.h>

// ピン接続 | Pin assign
#define GT_DTR 4       // DTR
#define GT_DSR 6       // DSR
#define GT_TRDY 7      // TRDY <--未使用 | Disused
#define SENSOR_PIN A0  // 光センサ接続ピン | Light Sensor Pin

// 変数設定 | Variable settings
int current_brightness = -1;       // 現在の輝度 | Current brightness
const int min_brightness = 3;      // 最小輝度 (約1.4%) | Minimum brightness
const int sensorMin = 100;         // センサ最小値 | Sensor minimum value
const int sensorMax = 1000;        // センサ最大値 | Sensor maximum value

// 最後に同期した時間を記録する変数 | Variable to record the last synchronized time
unsigned long last_sync_time = 0;

void setup() {
  // ピン初期設定 | Pin Initialization
  pinMode(GT_DTR, INPUT);
  pinMode(GT_DSR, OUTPUT);
  pinMode(GT_TRDY, INPUT);
  digitalWrite(GT_DSR, LOW);

  // シリアル設定 | Serial setting
  Serial.begin(38400);      // 38400bps Baud rate
  Serial.setTimeout(100);   // シリアルタイムアウト | Serial timeout

  // GT-SPのシステムが完全に立ち上がるまで待機する | Wait until the GT-SP system fully starts up
  delay(1500);
}

void loop() {
  // センサの平均値を読み取る | Read average sensor value
  int sensorValue = readAverageSensor();
  
  // センサの値を最小値から最大値の範囲内に収める | Constrain sensor value within min and max limits
  sensorValue = constrain(sensorValue, sensorMin, sensorMax);

  // センサの値を輝度（最小輝度から255）に変換する | Map sensor value to brightness (min_brightness to 255)
  int targetBrightness = map(sensorValue, sensorMin, sensorMax, min_brightness, 255);

  // 目標の輝度と現在の輝度との差分を計算する | Calculate the difference between target and current brightness
  int diff = targetBrightness - current_brightness;
  
  // 現在の時間を取得する | Get current time
  unsigned long current_time = millis();

  // 輝度に変化がある場合は更新処理へ | Go to update process if brightness has changed
  if (diff != 0) {
    update_brightness(diff);
    last_sync_time = current_time;
  } 
  // 変化がなくても5秒経過したら再送信（同期） | Resend every 5 seconds even if no change (sync)
  else if (current_time - last_sync_time >= 5000) {
    update_brightness(0);
    last_sync_time = current_time;
  }

  // ループの待機時間 | Loop delay
  delay(20);
}

/********************** カスタム関数 | Custom Functions **********************/

// 10回の平均をとって数値を安定させる関数 | Function to stabilize the value by taking the average of 10 times
int readAverageSensor() {
  long total = 0;

  // 10回センサの値を読み取って合計する | Read sensor value 10 times and sum them
  for (int i = 0; i < 10; i++) {
    total += analogRead(SENSOR_PIN);

    // 0.002秒の待機 | 0.002 sec delay
    delay(2);
  }

  // 10で割って平均値を返す | Divide by 10 and return the average
  return total / 10;
}

// 輝度を更新して送信する関数 | Function to update brightness and send
void update_brightness(int change_val) {
  // 現在の輝度に差分を足す | Add the difference to current brightness
  current_brightness = current_brightness + change_val;

  // 輝度が255を超えないように上限を設定 | Set upper limit so brightness does not exceed 255
  if (current_brightness > 255) {
    current_brightness = 255;
  }

  // 輝度が最小値を下回らないように下限を設定 | Set lower limit so brightness does not fall below minimum
  if (current_brightness < min_brightness) {
    current_brightness = min_brightness;
  }

  // GT-SPへ輝度を送信 | Send brightness to GT-SP
  set_gtsp_brightness(current_brightness);
}

/********************** GT-SP関数 | Function for GT-SP **********************/

////////////////////////////////////////////////////
// オブジェクト制御コマンド - プロパティ設定（数値用）
// Object Control Command - Property Setting (for Value)
////////////////////////////////////////////////////
void gtsp_ObjPrpSet_val(int obj, int prp, int ln, unsigned long val) {
  gt_print("CMD");   // コマンドヘッダ | Command header
  gt_put(0xd3);      // オブジェクト-プロパティ設定コマンド | Object-Property Setting
  gt_put(obj >> 0);  // オブジェクトNo. 下位バイト | Object No. Lower byte
  gt_put(obj >> 8);  // オブジェクトNo. 上位バイト | Object No. Upper byte
  gt_put(prp >> 0);  // プロパティNo. 下位バイト | Property No. Lower byte
  gt_put(prp >> 8);  // プロパティNo. 上位バイト | Property No. Upper byte
  gt_put(ln >> 0);   // データ長 最下位バイト | Data length Least significant byte
  gt_put(ln >> 8);   // データ長 下位バイト | Data length second byte
  gt_put(ln >> 16);  // データ長 上位バイト | Data length third byte
  gt_put(ln >> 24);  // データ長 最上位バイト | Data length Most significant byte

  if (ln >= 1) {
    gt_put(val >> 0);
  }

  if (ln >= 2) {
    gt_put(val >> 8);
  }

  if (ln >= 3) {
    gt_put(val >> 16);
  }

  if (ln >= 4) {
    gt_put(val >> 24);
  }
}

////////////////////////////////////////////////////
// オブジェクト制御コマンド - プロパティ設定（文字列用）
// Object Control Command - Property Setting (for String)
////////////////////////////////////////////////////
void gtsp_ObjPrpSet_string(int obj, int prp, String val) {
  gt_print("CMD");          // コマンドヘッダ | Command header
  gt_put(0xd3);             // オブジェクト-プロパティ設定コマンド | Object-Property Setting
  gt_put(obj >> 0);         // オブジェクトNo. 下位バイト | Object No. Lower byte
  gt_put(obj >> 8);         // オブジェクトNo. 上位バイト | Object No. Upper byte
  gt_put(prp >> 0);         // プロパティNo. 下位バイト | Property No. Lower byte
  gt_put(prp >> 8);         // プロパティNo. 上位バイト | Property No. Upper byte
  gt_put(val.length() >> 0);   // データ長 最下位バイト | Data length Least significant byte
  gt_put(val.length() >> 8);   // データ長 下位バイト | Data length second byte
  gt_put(val.length() >> 16);  // データ長 上位バイト | Data length third byte
  gt_put(val.length() >> 24);  // データ長 最上位バイト | Data length Most significant byte
  gt_print(val);               // 文字列送信 | Send string
}

////////////////////////////////////////////////////
// 文字列送信 | Send string to GT-SP
////////////////////////////////////////////////////
void gt_print(String val) {
  int val_i;

  // 文字列を1文字ずつ送信 | Send string per one byte
  for (val_i = 0; val_i < val.length(); val_i++) {
    while (digitalRead(GT_DTR) == HIGH) {
      // ビジーチェック | Busy check
    }

    Serial.print(val.substring(val_i, val_i + 1));
  }
}

////////////////////////////////////////////////////
// 1byte送信 | Send byte to GT-SP
////////////////////////////////////////////////////
void gt_put(unsigned char onebyte) {
  while (digitalRead(GT_DTR) == HIGH) {
    // ビジーチェック | Busy check
  }

  Serial.write(onebyte);
}

////////////////////////////////////////////////////
// GT-SPへ輝度設定コマンドを送信する関数
// Function to send brightness setting command to GT-SP
////////////////////////////////////////////////////
void set_gtsp_brightness(int val) {
  gt_print("CMD");
  gt_put(0x58);
  gt_put(val);
}
