/* Copyright (C) 2014-2021 m-privacy GmbH
 * 
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>

#include <list>

#include <rdr/types.h>
#include <rfb/encodings.h>
#include <rfb/PixelFormat.h>

#include "CConn.h"
#include "StreamDialog.h"
#include "fltk_layout.h"
#include "i18n.h"
#include "menukey.h"
#include "Viewport.h"
#include "parameters.h"
#include "sound_handler.h"

#include <FL/Fl_Tabs.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Round_Button.H>
#include <FL/Fl_Int_Input.H>
#include <FL/Fl_Choice.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Box.H>

#include <rfb/LogWriter.h>
#include <rfb/CWebcamHandler.h>

using namespace std;
using namespace rdr;
using namespace rfb;

static rfb::LogWriter vlog("StreamDialog");

static char msg[256];

inline char* get_dialog_title() {
  snprintf(msg, scale(256), _("%s: Webcam and Microphone"), productName.getData());
  return msg;
}

StreamDialog::StreamDialog(CConn* cc_)
  : Fl_Window(scale(400), scale(400), get_dialog_title()), cc(cc_), oldMicSupport(micSupport)
{
  int x, y;
  Fl_Button *button;

  webcamCount = 0;
  webcamList = NULL;
  micCount = 0;
  micList = NULL;
  webcamSizeCount = 0;
  webcamSizeList = NULL;

  Fl_Tabs *tabs = new Fl_Tabs(OUTER_MARGIN, OUTER_MARGIN,
                             w() - OUTER_MARGIN*2,
                             h() - OUTER_MARGIN*2 - INNER_MARGIN - BUTTON_HEIGHT);

  {
    int tx, ty, tw, th;

    tabs->client_area(tx, ty, tw, th, TABS_HEIGHT);

    createDevicePage(tx, ty, tw, th);
  }

  tabs->end();

  x = w() - BUTTON_WIDTH * 3.1 - INNER_MARGIN - OUTER_MARGIN;
  y = h() - BUTTON_HEIGHT - OUTER_MARGIN;

  button = new Fl_Button(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, _("Cancel"));
  button->callback(this->handleCancel, this);

  x += BUTTON_WIDTH + INNER_MARGIN;

  button = new Fl_Button(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, _("Apply"));
  button->callback(this->handleApply, this);

  x += BUTTON_WIDTH + INNER_MARGIN;

  button = new Fl_Return_Button(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, _("OK"));
  button->callback(this->handleOK, this);

  x += BUTTON_WIDTH + INNER_MARGIN;

  scaleWidgetFonts(this);

  set_modal();
}


StreamDialog::~StreamDialog()
{
  if (webcamCount > 0 && webcamList)
    free(webcamList);
  if (micCount > 0 && micList)
    free(micList);
  if (webcamSizeCount > 0 && webcamSizeList)
    free(webcamSizeList);
}

void StreamDialog::showDialog(CConn* cc)
{
  static StreamDialog *dialog = NULL;
#ifdef WIN32
  POINT cursorPos;
#endif

  if (!dialog)
    dialog = new StreamDialog(cc);
  else {
    dialog->fillWebcamChoice();
  }

#ifdef WIN32
  if(GetCursorPos(&cursorPos))
    dialog->position(cursorPos.x, cursorPos.y);
#endif

  if (dialog->shown())
    return;
  dialog->show();
}

void StreamDialog::show(void)
{
  if (!shown())
    loadOptions();

  Fl_Window::show();
}

void StreamDialog::loadOptions(void)
{
  /* Stream menu setting, on load page*/
  const char *webcamBuf;
#if 0
  const char *micBuf;
#endif
  const char *webcamSizeBuf;

#ifdef WIN32
#if 0
  if (micCount > 0) {
    micCheckbox->activate();
    micChoice->label(_("Choose microphone"));
  } else {
    micCheckbox->deactivate();
    micEnabled.setParam(false);
    micChoice->label(_("No microphone"));
  }
  micCheckbox->value(micEnabled);
#endif
  micCheckbox->value(micSupport);
#endif

#ifdef WIN32
#if 0
  micChoice->value(micSelect);
  micBuf = micName;
  for (int i = 0; i < micCount; i++)
    if (!strcmp(micList[i].name, micBuf)) {
      micChoice->value(i);
      break;
    }
  handleMic(micCheckbox, this);
#endif
#endif
  webcamSizeBuf = webcamSize;
  webcamSizeChoice->value(webcamSizeSelect);
  for (int i = 0; i < webcamSizeCount; i++)
    if (!strcmp(webcamSizeList[i].name, webcamSizeBuf)) {
      webcamSizeChoice->value(i);
      break;
    }
  if (webcamCount > 0) {
    webcamCheckbox->activate();
    webcamChoice->label(_("Choose webcam"));
  } else {
    webcamCheckbox->deactivate();
    webcamEnabled.setParam(false);
    webcamChoice->label(_("No webcam"));
  }
  webcamCheckbox->value(webcamEnabled);

  webcamChoice->value(webcamSelect);
  webcamBuf = webcamName;
  for (int i = 0; i < webcamCount; i++)
    if (!strcmp(webcamList[i].name, webcamBuf)) {
      webcamChoice->value(i);
      break;
    }
  handleWebcam(webcamCheckbox, this);
}

void StreamDialog::storeOptions(void)
{
  /* Stream menu setting, on save/apply */
  
  if (webcamChoice->text())
    webcamName.setParam(webcamChoice->text());
  webcamEnabled.setParam(webcamCheckbox->value());
  webcamSelect.setParam(webcamChoice->value());

  if (webcamSizeChoice->text())
    webcamSize.setParam(webcamSizeChoice->text());
  webcamSizeSelect.setParam(webcamSizeChoice->value());

#ifdef WIN32
#if 0
  if (micChoice->text())
    micName.setParam(micChoice->text());
  micEnabled.setParam(micCheckbox->value());
  micSelect.setParam(micChoice->value());
#endif

  micSupport.setParam(micCheckbox->value());
  if (soundSupport && oldMicSupport != micSupport) {
    oldMicSupport = micSupport;
    cc->setMicSupport(micCheckbox->value());
    vlog.debug("micSupport changed, restarting sound");
    sound::stop_sound();
    if (!sound::start_sound(cc->getPaPort()) && micSupport) {
      vlog.error("Failed to start sound with microphone support. Trying to start it without microphone support");
      micSupport.setParam(false);
      cc->setMicSupport(false);
      if (!sound::start_sound(cc->getPaPort())) {
        vlog.error("Failed to restart sound, disabling soundSupport");
        soundSupport.setParam(false);
        cc->setSoundSupport(false);
      }
    }
  }
#endif
}

void StreamDialog::fillWebcamChoice()
{
  if (webcamCount > 0 && webcamList)
    free(webcamList);
  webcamCount = Viewport::getInputDeviceList(&webcamList, true);
  webcamChoice->clear();
  if (webcamCount > 0) {
    for (int i = 0; i < webcamCount; i++) {
      fltk_menu_add(webcamChoice, webcamList[i].name, 0, NULL, 0, 0);
    }
    webcamCheckbox->activate();
    webcamChoice->label(_("Choose webcam"));
  } else {
    webcamCheckbox->deactivate();
    webcamEnabled.setParam(false);
    webcamChoice->label(_("No webcam"));
  }
#ifdef WIN32
  if (soundSupport) {
    micCheckbox->activate();
  } else {
    micSupport.setParam(false);
    cc->setMicSupport(false);
    micCheckbox->deactivate();
  }
#endif
}

static int getWebcamSizeList(struct AVDevice ** deviceListPointer)
{
  int count = 0;
  struct AVDevice * deviceList;

  deviceList = (struct AVDevice *) malloc(MAXWEBCAMNUM * sizeof(struct AVDevice));
  if (!deviceList) {
    vlog.error("getWebcamSizeList: failed to allocate memory");
    return -2;
  }
  sprintf(deviceList[count++].name, "640x360");
  sprintf(deviceList[count++].name, "960x540");
  sprintf(deviceList[count++].name, "1280x720");
  sprintf(deviceList[count++].name, "1920x1080");
  sprintf(deviceList[count++].name, "MAX");
  *deviceListPointer = deviceList;
  return count;
}

void StreamDialog::createDevicePage(int tx, int ty, int tw, int th)
{
    Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Webcam and Microphone"));

  int orig_tx;
  int width, height;

  tx += OUTER_MARGIN;
  ty += OUTER_MARGIN;

  width = tw - OUTER_MARGIN * 2;
  orig_tx = tx;

  /* Stream setting */
  ty += GROUP_LABEL_OFFSET;
  height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 2 + CHECK_HEIGHT * 1 + CHOICE_HEIGHT * 2;

  videoGroup = new Fl_Group(tx, ty, width, height, _("Webcam"));
  videoGroup->box(FL_ENGRAVED_BOX);
  videoGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  tx += GROUP_MARGIN;
  ty += GROUP_MARGIN;
  webcamCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, CHECK_MIN_WIDTH, CHECK_HEIGHT, _("Activate webcam")));
  webcamCheckbox->callback(handleWebcam, this);
  ty += CHECK_HEIGHT + TIGHT_MARGIN;
  webcamChoice = new Fl_Choice(LBLLEFT(tx, ty, 300, CHOICE_HEIGHT, _("Choose webcam")));
  ty += CHOICE_HEIGHT + TIGHT_MARGIN;
  webcamSizeChoice = new Fl_Choice(LBLLEFT(tx, ty, 300, CHOICE_HEIGHT, _("Choose resolution")));
  if (webcamSizeCount > 0 && webcamSizeList)
    free(webcamSizeList);
  webcamSizeCount = getWebcamSizeList(&webcamSizeList);
  for (int i = 0; i < webcamSizeCount; i++) {
    fltk_menu_add(webcamSizeChoice, webcamSizeList[i].name, 0, NULL, 0, 0);
  }
  ty += CHOICE_HEIGHT + TIGHT_MARGIN;
  ty += GROUP_MARGIN + TIGHT_MARGIN;
  videoGroup->end();

#ifdef WIN32
  tx -= GROUP_MARGIN;
  ty += GROUP_LABEL_OFFSET + TIGHT_MARGIN;
  height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 2 + CHECK_HEIGHT * 1;
  micGroup = new Fl_Group(tx, ty, width, height, _("Microphone"));
  micGroup->box(FL_ENGRAVED_BOX);
  micGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  tx += GROUP_MARGIN;
  ty += GROUP_MARGIN;
  micCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, CHECK_MIN_WIDTH, CHECK_HEIGHT, _("Enable microphone")));
#if 0
  micCheckbox->callback(handleMic, this);
  ty += CHECK_HEIGHT + TIGHT_MARGIN;
  micChoice = new Fl_Choice(LBLLEFT(tx, ty, 300, CHOICE_HEIGHT, _("Choose microphone")));
  if (micCount > 0 && micList)
    free(micList);
  micCount = Viewport::getInputDeviceList(&micList, false);
  if (micCount > 0) {
    for (int i = 0; i < micCount; i++) {
      fltk_menu_add(micChoice, micList[i].name, 0, NULL, 0, 0);
      ty += CHECK_HEIGHT + TIGHT_MARGIN;
    }
  }
#endif
  ty += GROUP_MARGIN - TIGHT_MARGIN;
  micGroup->end();
#endif


  fillWebcamChoice();

  tx = orig_tx;
  ty += INNER_MARGIN;
  
  group->end();
}


void StreamDialog::handleWebcam(Fl_Widget *widget, void *data)
{
  StreamDialog *dialog = (StreamDialog*)data;

  if (dialog->webcamCheckbox->value()) {
    dialog->webcamChoice->activate();
    dialog->webcamSizeChoice->activate();
#ifdef WIN32
#if 0
    if (dialog->micCount > 0) {
      dialog->micCheckbox->activate();
      if (dialog->micCheckbox->value())
        dialog->micChoice->activate();
      else
        dialog->micChoice->deactivate();
    } else {
      dialog->micCheckbox->deactivate();
      dialog->micChoice->deactivate();
    }
#endif
#endif
  } else {
    dialog->webcamChoice->deactivate();
    dialog->webcamSizeChoice->deactivate();
#ifdef WIN32
#if 0
    dialog->micCheckbox->deactivate();
    dialog->micChoice->deactivate();
#endif
#endif
  }
}

#ifdef WIN32
#if 0
void StreamDialog::handleMic(Fl_Widget *widget, void *data)
{
  StreamDialog *dialog = (StreamDialog*)data;

  if (dialog->micCheckbox->value())
    dialog->micChoice->activate();
  else
    dialog->micChoice->deactivate();
}
#endif
#endif

void StreamDialog::handleCancel(Fl_Widget *widget, void *data)
{
  StreamDialog *dialog = (StreamDialog*)data;

  dialog->hide();
}


void StreamDialog::handleOK(Fl_Widget *widget, void *data)
{

  StreamDialog *dialog = (StreamDialog*)data;
  dialog->storeOptions();
  if (webcamEnabled) {
      vlog.debug("Enabling webcam (starting capture)");
      TGVNC_CONDITION_SEND_SIG(&CWebcamHandler::webcamEnabledCondition);
  } else {
      vlog.debug("Disabling webcam (stopping capture)");
      TGVNC_CONDITION_SEND_SIG(&CWebcamHandler::webcamEnabledCondition);
  } 
  dialog->hide();
}

void StreamDialog::handleApply(Fl_Widget *widget, void *data)
{
  StreamDialog *dialog = (StreamDialog*)data;

  dialog->storeOptions();
  if (webcamEnabled) {
      vlog.debug("Enabling webcam (starting capture)");
      TGVNC_CONDITION_SEND_SIG(&CWebcamHandler::webcamEnabledCondition);
  }else{
      vlog.debug("Disabling webcam (stopping capture)");
      TGVNC_CONDITION_SEND_SIG(&CWebcamHandler::webcamEnabledCondition);
  } 
}
