/*!
  \file
  \brief SH7045F の Flash-ROM 書き込みプログラム

  \author Satofumi KAMIMURA

  $Id: sh7045writer.cpp 1157 2009-07-18 11:18:40Z satofumi $
*/

#include "FZtatHandler.h"
#include "SformatHandler.h"
#include "SformatSender.h"
#include "writer_program.h"
#include "stub_sh7045.h"
#include "SerialDevice.h"
#include "TcpipSocket.h"
#include "Thread.h"
#include "DetectOS.h"
#include <iostream>
#include <cstdlib>
#include <cstring>

using namespace qrk;
using namespace std;


namespace
{
  enum {
    Port = 7045,
  };

#if defined(WINDOWS_OS)
  const char* DefaultPort = "COM3";
#else
  const char* DefaultPort = "/dev/usb/ttyUSB0";
#endif

  Thread* thread_ = NULL;


  class ArgsInformation
  {
    string device_;
    string file_;
    bool simulation_;


    void printHelp(const char* program_name)
    {
      cout << "usage: " << endl
           << "\t" << program_name << " [options] <mot file>" << endl;

      // !!! options

      exit(0);
    }


  public:
    ArgsInformation(int argc, char *argv[])
      : device_(DefaultPort), simulation_(false)
    {
      // -h, --help
      // -v, --version
      for (int i = 1; i < argc; ++i) {
        const char* token = argv[i];
        if (! strncmp("--port=", token, 6)) {
          device_ = &token[7];

        } else if ((! strcmp("-s", token)) ||
                   (! strcmp("--simulation", token))) {
          simulation_ = true;

        } else {
          file_ = token;
        }
      }

      // 引数に何も指定がない場合、ヘルプを表示して終了する
      const char* program_name = argv[0];
      if (file_.empty()) {
        printHelp(program_name);
      }
    }


    const char* device(void) const
    {
      return device_.c_str();
    }


    const char* file(void) const
    {
      return file_.c_str();
    }


    bool isSimulation(void) const
    {
      return simulation_;
    }
  };


  class FZtatHandlerConsole : public FZtatHandler
  {
  public:
    void progress(double percent)
    {
      static_cast<void>(percent);
      cout << ".";
    }
  };


  class SformatSenderConsole : public SformatSender
  {
  public:
    SformatSenderConsole(Connection* connection, long baudrate)
      : SformatSender(connection, baudrate)
    {
    }


    void progress(double percent)
    {
      static_cast<void>(percent);
      cout << ".";
    }
  };


  void runStub(void)
  {
    long port = Port;
    thread_ = new Thread(stub_sh7045, &port);
    thread_->run();
  }
}


int main(int argc, char *argv[])
{
  // 接続ポートを引数で受け取る
  ArgsInformation args(argc, argv);

  // mot ファイルをチェック
  SformatHandler mot_file(args.file());
  if (mot_file.invalid()) {
    cout << mot_file.what() << endl;
    exit(1);
  }

  Connection* connection;
  if (args.isSimulation()) {
    // スタブに接続
    runStub();
    connection = new TcpipSocket;
    if (! connection->connect("localhost", Port)) {
      cout << "Coudn't connect: localhost, " << Port << endl;
      exit(1);
    }
  } else {
    // デバイスに接続
    connection = new SerialDevice;
    enum { FirstBaudrate = 9600 };
    if (! connection->connect(args.device(), FirstBaudrate)) {
      cout << "Coudn't connect: " << args.device() << endl;
      exit(1);
    }
  }

  // F-ZTAT に従って書き込み用のプログラムを転送
  FZtatHandlerConsole fztat;
  size_t send_size = sizeof(writer_program) - 1;
  if (! fztat.transfer(connection, writer_program, send_size)) {
    cout << fztat.what() << endl;
    exit(1);
  }

  // 独自プロトコルで mot ファイルを転送して書き込み
  enum { TransferBaudrate = 115200 };
  SformatSenderConsole sformat(connection, TransferBaudrate);
  if (! sformat.send(args.file())) {
    cout << sformat.what() << endl;
    exit(1);
  }

  cout << "exit normal." << endl;

  delete connection;
  return 0;
}
