import { Panel } from "./panel";
import { findServices, installService, getErrorMessage, eventHub, uninstallService, globular } from "./backend";
import { ServiceDescriptor } from "globular-web-client/lib/services/services_pb";
import { randomUUID } from "./utility";

/**
 * Search panel is use to retreive services on registerd discoveries.
 */
export class SearchServicesPanel extends Panel {
  private resultsPanel: any;
  private isAdmin: boolean;

  constructor() {
    super("search_panel");
    // That will contain the results of the actual search.
    this.resultsPanel = this.div.appendElement({ tag: "div" }).down();
    this.isAdmin = false;
  }

  /**
   * That function is call when the user set press the enter button.
   * @param keywords
   */
  search(keywords: Array<string>) {
    this.resultsPanel.removeAllChilds();
    findServices(keywords, (services: Array<ServiceDescriptor>) => {
      // First of all I will regroup service by their publisher id and
      // service id.
      let servicesMap = new Map<String, Array<ServiceDescriptor>>();
      for (var i = 0; i < services.length; i++) {
        let id = services[i].getPublisherid() + "_" + services[i].getId()
        if (!servicesMap.has(id)) {
          servicesMap.set(id, new Array<ServiceDescriptor>())
        }
        servicesMap.get(id).push(services[i])
      }

      // Now I will display the services.
      servicesMap.forEach((descriptors: Array<ServiceDescriptor>) => {
        // sort by version number.
        descriptors.sort((a, b) => (a.getVersion() > b.getVersion()) ? 1 : ((b.getVersion() > a.getVersion()) ? -1 : 0))
        let descriptorPanel = new ServiceDescriptorPanel(descriptors);
        descriptorPanel.setParent(this.resultsPanel);
        if (this.isAdmin) {
          descriptorPanel.onlogin(globular.config);
        }
      })
    },
    (err: any) => {
      M.toast({ html: getErrorMessage(err.message), displayLength: 2000 });
    });
  }

  onlogin(data: any) {
    this.isAdmin = true;
  }

  onlogout() {
    this.isAdmin = false;
  }
}

/**
 * Display the description of a services.
 */
class ServiceDescriptorPanel extends Panel {
  private descriptors: Array<ServiceDescriptor>;
  private index: number;
  private content: any;
  private installBtn: any;
  private uninstallBtn: any;
  private updateBtn: any;
  private btnGroup: any;
  private idInput: any;
  private idDiv: any;
  private versionDiv: any;

  constructor(descriptors: Array<ServiceDescriptor>) {
    // Set the panel id.
    super(
      "service_description_panel_" + randomUUID()
    );

    // keep track of the service diplayed.
    this.descriptors = descriptors;
    this.index = this.descriptors.length - 1; // by default display the last available version.

    // Set the index to install service version if any...
    this.setCurrentIndex()

    let install_btn_id = randomUUID()
    let id_input = randomUUID()
    let id_div = randomUUID()
    let uninstall_btn_id = randomUUID()
    let update_btn_id = randomUUID()

    // In case the service is uninstall...
    eventHub.subscribe(
      "uninstall_service_event",
      (uuid: string) => {
        //console.log("start_service_event_" + id, uuid);
      },
      (evt: any) => {
        this.setButtons()
      },
      true
    );

    eventHub.subscribe(
      "install_service_event",
      (uuid: string) => {
        //console.log("start_service_event_" + id, uuid);
      },
      (evt: any) => {
        this.setButtons()
      },
      true
    );

    // get the service descriptor.
    let descriptor = this.getServiceDescriptor();


    // Display general information.
    this.div
      .appendElement({ tag: "div", class: "row service_descriptor_panel" })
      .down()
      .appendElement({ tag: "div", class: "col s12 /*m10 offset-m1*/" })
      .down()
      .appendElement({ tag: "div", class: "card" })
      .down()
      .appendElement({ tag: "div", class: "card-content" })
      .down()
      .appendElement({
        tag: "span",
        class: "card-title",
        style: "font-size: medium; font-weight: inherit;",
        innerHtml: descriptor.getId()
      })
      .appendElement({ tag: "div", id: "content" })
      // The action buttons.
      .appendElement({
        tag: "div",
        class: "card-action row",
        id: "btn_group",
        style: "text-align: right; display: none; align-items: baseline; justify-content: flex-end;"
      })
      .down()
      .appendElement({ tag: "div", id: id_div, class: "input-field col s4 offset-s6" })
      .down()
      .appendElement({
        tag: "input",
        id: id_input,
        syle: "margin-left: 0px;",
        placeholder: "service id",
        title: "the is of the service on the server.",
        type: "text"
      })
      .appendElement({ tag: "label", for: id_input, innerHtml: "service id" })
      .up()
      .appendElement({
        tag: "a",
        id: install_btn_id,
        href: "javascript:void(0)",
        class: "waves-effect waves-light btn col s2",
        syle: "margin-left: 0px;",
        innerHtml: "Install"
      })
      .appendElement({
        tag: "a",
        id: update_btn_id,
        href: "javascript:void(0)",
        class: "waves-effect waves-light btn col s2",
        style: "display: none; margin-left: 0px;",
        innerHtml: "Update"
      })
      .appendElement({
        tag: "a",
        id: uninstall_btn_id,
        href: "javascript:void(0)",
        class: "waves-effect waves-light btn col s2",
        style: "display: none; margin-left: 0px;",
        innerHtml: "Uninstall"
      })


    this.installBtn = this.div.getChildById(install_btn_id);
    this.uninstallBtn = this.div.getChildById(uninstall_btn_id);
    this.updateBtn = this.div.getChildById(update_btn_id);
    this.content = this.div.getChildById("content");
    this.btnGroup = this.div.getChildById("btn_group");
    this.idInput = this.div.getChildById(id_input);
    this.idDiv = this.div.getChildById(id_div);

    let version_div_id = randomUUID()

    // Display the publisher id.
    this.content
      .appendElement({ tag: "div", class: "row" })
      .down()
      .appendElement({
        tag: "div",
        class: "col s12 m6",
        style: "height: 100%",
        innerHtml: "Publisher"
      })
      .appendElement({
        tag: "div",
        id: "publisher_div",
        class: "col s12 m6",
        innerHtml: descriptor.getPublisherid()
      })
      .down();

    this.versionDiv = this.content
      .appendElement({ tag: "div", class: "row" })
      .down()
      .appendElement({
        tag: "div",
        class: "col s12 m6",
        style: "height: 100%",
        innerHtml: "Version"
      })
      .appendElement({
        tag: "div",
        id: version_div_id,
        class: "col s12 m6",
        innerHtml: descriptor.getVersion()
      })
      .down();

    this.content
      .appendElement({ tag: "div", class: "row" })
      .down()
      .appendElement({
        tag: "div",
        class: "col s12 m6",
        style: "height: 100%",
        innerHtml: "Description"
      })
      .appendElement({
        tag: "div",
        id: "description_div",
        class: "col s12 m6",
        innerHtml: descriptor.getDescription()
      })
      .down();



    // Now actions...
    this.installBtn.element.onclick = () => {

      // get the service descriptor.
      let descriptor = this.getServiceDescriptor();

      installService(
        descriptor.getDiscoveriesList()[0],
        descriptor.getId(),
        descriptor.getPublisherid(),
        descriptor.getVersion(),
        () => {
          // here I will refresh the token and set the full config...
          eventHub.publish("install_service_event", descriptor.getId(), true)
          M.toast({ html: "Service " + descriptor.getId() + " installed successfully!", displayLength: 3000 });
          // refresh the panel again to set the new service to admin mode.
          eventHub.publish("onlogin", globular.config, true)
        },
        (err: any) => {
          M.toast({ html: getErrorMessage(err.message), displayLength: 2000 });
        }
      );
    }

    this.idInput.element.onkeyup = (evt: any) => {
      let value = this.idInput.element.value.replace(/\s/g, "");

      if (value.length > 0) {
        this.installBtn.element.classList.remove("disabled");
      } else {
        this.installBtn.element.classList.add("disabled");
      }

      // get the service descriptor.
      let descriptor = this.getServiceDescriptor();

      if (evt.keyCode == 13) {
        installService(
          descriptor.getDiscoveriesList()[0],
          descriptor.getId(),
          descriptor.getPublisherid(),
          descriptor.getVersion(),
          () => {
            eventHub.publish("install_service_event", descriptor.getId(), true)
            M.toast({ html: "Service " + descriptor.getId() + " installed successfully!", displayLength: 3000 });
            // refresh the panel again to set the new service to admin mode.
            eventHub.publish("onlogin", globular.config, true)
          },
          (err: any) => {
            M.toast({ html: getErrorMessage(err.message), displayLength: 2000 });
          }
        );
      }
    };
  }

  setCurrentIndex() {
    // Set the index to install service version if any...
    for (var i = 0; i < this.descriptors.length; i++) {
      if (globular.config.Services[this.descriptors[i].getId()] != undefined) {
        if (globular.config.Services[this.descriptors[i].getId()].PublisherId == this.descriptors[i].getPublisherid()) {
          if (globular.config.Services[this.descriptors[i].getId()].Version == this.descriptors[i].getVersion()) {
            this.index = i;
            break;
          }
        }
      }
    }
  }

  getServiceDescriptor(): ServiceDescriptor {
    let descriptor: ServiceDescriptor
    descriptor = this.descriptors[this.index]; // get the last element.
    return descriptor;
  }

  setButtons() {
    // Display textual input
    this.btnGroup.element.style.display = "flex";

    // look better without it... uncomment if necessary.
    M.updateTextFields();
    this.idInput.element.value = this.getServiceDescriptor().getId();

    if (globular.config.Services[this.getServiceDescriptor().getId()] != undefined) {

      // if the service is already install.
      let service = globular.config.Services[this.getServiceDescriptor().getId()];
      this.uninstallBtn.element.style.display = "block";

      if (this.getServiceDescriptor().getVersion() != service.Version) {
        this.updateBtn.element.style.display = "block";
      } else {
        this.updateBtn.element.style.display = "none";
      }

      this.installBtn.element.style.display = "none";
      this.idDiv.element.style.display = "none";

      // The unistall action.
      this.uninstallBtn.element.onclick = (evt: any) => {
        evt.stopPropagation();
        uninstallService(service,
          true, () => {
            eventHub.publish("uninstall_service_event", this.getServiceDescriptor().getId(), true)
            M.toast({ html: "Service " + this.getServiceDescriptor().getId() + " was uninstalled successfully!", displayLength: 3000 });
          },
          (err: any) => {

            M.toast({ html: getErrorMessage(err.message), displayLength: 2000 });
          });
      };

      // The update action.
      this.updateBtn.element.onclick = (evt: any) => {
        evt.stopPropagation();
        uninstallService(service, false,
          () => {
            // Now I will simply install the service.
            this.installBtn.element.click();
            this.updateBtn.element.style.display = "none"
          },
          (err: any) => {

            M.toast({ html: getErrorMessage(err.message), displayLength: 2000 });
          });
      }
    } else {
      this.installBtn.element.style.display = "";
      this.idDiv.element.style.display = "";
      this.uninstallBtn.element.style.display = "none";
      this.updateBtn.element.style.display = "none";
    }

    // So here I will get the list of available verisons and diplay it in a select element.
    if (this.descriptors.length > 0) {
      this.versionDiv.removeAllChilds()
      this.versionDiv.element.innerHTML = ""
      let versionSelector = this.versionDiv.appendElement({ tag: "select", style: "display: block;" }).down()
      this.descriptors.forEach((descriptor: ServiceDescriptor, index) => {
        versionSelector.appendElement({ tag: "option", value: index, innerHtml: descriptor.getVersion() })
      })

      // Set the value to current version...
      versionSelector.element.value = this.index;

      versionSelector.element.onchange = () => {
        this.index = versionSelector.element.value
        this.setButtons()
      }

      versionSelector.element.onclick = (evt: any)=>{
        evt.stopPropagation()
      }
    }
  }

  onlogin(data: any) {
    // keep a pointer to the full configuration.
    this.setButtons()
  }

  onlogout() {
    // display values.
    this.btnGroup.element.style.display = "none";
    this.setCurrentIndex()
    this.versionDiv.element.innerHTML = this.getServiceDescriptor().getVersion() // set back the actual version.
  }
}
