import { Mission } from "../Aerofly/Mission.js";
import { MissionCheckpoint } from "../Aerofly/MissionCheckpoint.js";
import { MissionConditions, MissionConditionsCloud } from "../Aerofly/MissionConditions.js";
import { Outputtable } from "../Export/Outputtable.js";
import { Quote } from "../Export/Quote.js";
import { SkyVector } from "../Export/SkyVector.js";
import { LonLatArea } from "../World/LonLat.js";
import { LonLatDate, LonLateDateSunState } from "../World/LonLatDate.js";

class ComponentsOutputtable extends HTMLElement {
  mission?: Mission;

  elements = {
    table: <HTMLTableElement>document.createElement("table"),
    caption: <HTMLTableCaptionElement>document.createElement("caption"),
    thead: <HTMLTableSectionElement>document.createElement("thead"),
    tbody: <HTMLTableSectionElement>document.createElement("tbody"),
  };

  constructor() {
    super();

    this.elements.table.appendChild(this.elements.caption);
    this.elements.table.appendChild(this.elements.thead);
    this.elements.table.appendChild(this.elements.tbody);
    this.appendChild(this.elements.table);
  }

  draw() {
    this.elements.table.classList.toggle("empty", !this.mission);
    if (!this.mission) {
      this.elements.tbody.innerHTML = "";
    }
  }

  /**
   * @param fields Table cell contents
   * @param join `td` or `th`; to supress a `th` at the beginnining of a `tr`
   *              with `td`s set it to `ttd`, which will be converted to `td`
   * @returns string
   */
  protected outputLine(fields: string[], join = "td"): string {
    const tag = join === "ttd" ? "td" : join;
    return join === "td"
      ? `<tr><th scope="row">` +
          fields[0] +
          `</th><${tag}>` +
          fields.slice(1).join(`</${tag}><${tag}>`) +
          `</${tag}></tr>`
      : `<tr><${tag}>` + fields.join(`</${tag}><${tag}>`) + `</${tag}></tr>`;
  }

  protected outputDateTime(date: Date) {
    return date.toISOString().replace(/:\d+\.\d+/, "");
  }

  protected outputSunState(sunState: LonLateDateSunState): string {
    const deg = (sunState.solarElevationAngleDeg / 6) * 5;
    const degX = Math.max(0, Math.min(18, 12 - deg));

    return `<svg width="20" height="20" version="1.1" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
    <title>${deg.toFixed()}°</title>
    <style>
      rect, circle, line { stroke-width: 1px; stroke: currentColor; fill: none; stroke-linecap: round; stroke-linejoin: round; }
    </style>
    <rect x="1" y="1" width="18" height="18" />
    <circle cx="10" cy="10" r="3" />
    <line x1="3" y1="10" x2="5" y2="10" />
    <line x1="3" y1="10" x2="5" y2="10" transform="rotate(45, 10, 10)" />
    <line x1="3" y1="10" x2="5" y2="10" transform="rotate(90, 10, 10)" />
    <line x1="3" y1="10" x2="5" y2="10" transform="rotate(135, 10, 10)" />
    <line x1="3" y1="10" x2="5" y2="10" transform="rotate(180, 10, 10)" />
    <line x1="3" y1="10" x2="5" y2="10" transform="rotate(225, 10, 10)" />
    <line x1="3" y1="10" x2="5" y2="10" transform="rotate(270, 10, 10)" />
    <line x1="3" y1="10" x2="5" y2="10" transform="rotate(315, 10, 10)" />
    <rect x="1" y="${19 - degX}" width="18" height="${degX}" style="fill: currentColor" />
  </svg> ${Quote.html(sunState.sunState)}`;
  }

  protected outputCover(cloud: MissionConditionsCloud): string {
    const octas = Math.round(cloud.cover * 8);
    let svg = `<svg width="20" height="20" version="1.1" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
    <style>
      circle, line, path { stroke-width: 1px; stroke: currentColor; fill: none; stroke-linecap: round; stroke-linejoin: round; }
      path { fill: currentColor; stroke: none; }
    </style>
    <title>${octas}/8</title>
    <circle cx="10" cy="10" r="9" />`;

    const middle = octas === 7 ? 0.5 : 0;
    if (octas > 0) {
      if (octas === 1 || octas === 3) {
        svg += '<line x1="10" y1="1" x2="10" y2="19" />';
      }
      if (octas === 5) {
        svg += '<line x1="1" y1="10" x2="19" y2="10" />';
      }
      if (octas >= 2) {
        svg += `<path d="m ${10 + middle},1 a ${9 - middle},${9 - middle} 0 0 1 9,9 h -9 z" />`;
      }
      if (octas >= 4) {
        svg += `<path d="m ${10 + middle},1 a ${9 - middle},${
          9 - middle
        } 0 0 1 9,9 h -9 z" transform="scale(1,-1) translate(0,-20)" />`;
      }
      if (octas >= 6) {
        svg += `<path d="m ${10 + middle},1 a ${9 - middle},${
          9 - middle
        } 0 0 1 9,9 h -9 z" transform="scale(-1,-1) translate(-20,-20)" />`;
      }
      if (octas >= 7) {
        svg += `<path d="m ${10 + middle},1 a ${9 - middle},${
          9 - middle
        } 0 0 1 9,9 h -9 z" transform="scale(-1,1) translate(-20, 0)" />`;
      }
    }

    svg += "</svg>";
    return svg;
  }

  protected getWind(conditions: MissionConditions): string {
    let wind_speed = conditions.wind_speed.toFixed();
    const gust_type = conditions.wind_gusts_type;
    if (gust_type) {
      wind_speed += "G" + conditions.wind_gusts.toFixed();
    }
    return Outputtable.padThree(conditions.wind_direction) + "° @ " + wind_speed;
  }
}

export class ComponentsWeather extends ComponentsOutputtable {
  constructor() {
    super();
    this.elements.caption.innerText = "Weather";
    this.elements.thead.innerHTML = this.outputLine(["Wind ", "Clouds", "Visibility", "Min flight rules"], "th");
    this.draw();
  }

  draw() {
    super.draw();

    const m = this.mission;
    if (!m) {
      return;
    }

    let html = "";
    html += this.outputLine(
      [
        Quote.html(this.getWind(m.conditions)) + "&nbsp;kts",
        this.outputCover(m.conditions.cloud) +
          "&nbsp;" +
          Quote.html(m.conditions.cloud.cover_code) +
          " @ " +
          m.conditions.cloud.height_feet.toLocaleString("en") +
          "&nbsp;ft",
        m.conditions.visibility.toLocaleString("en") +
          "&nbsp;m / " +
          Math.round(m.conditions.visibility_sm) +
          "&nbsp;SM",
        m.conditions.getFlightCategory(m.origin_country !== "US"),
      ],
      "ttd"
    );

    this.elements.tbody.innerHTML = html;
  }
}

export class ComponentsAirports extends ComponentsOutputtable {
  constructor() {
    super();
    this.elements.caption.innerText = "Airports";
    this.elements.thead.innerHTML = this.outputLine(
      ["Type", "Location ", "Country", "Date &amp; time ", '<abbr title="Local solar time">LST</abbr>', " Sun"],
      "th"
    );
    this.draw();
  }

  draw() {
    super.draw();

    const m = this.mission;
    if (!m) {
      return;
    }

    const total_time_enroute = m.time_enroute;
    const time = new Date(m.conditions.time.dateTime);
    const sunStateOrigin = new LonLatDate(m.origin_lon_lat, time).sunState;
    time.setSeconds(time.getSeconds() + total_time_enroute * 3600);
    const sunStateDestination = new LonLatDate(m.destination_lon_lat, time).sunState;

    let html = "";

    html += this.outputLine([
      "Departure",
      m.origin_icao
        ? `<a target="airport" href="${SkyVector.airportLink(m.origin_icao)}">${Quote.html(m.origin_icao)}</a>`
        : "",
      m.origin_country,
      this.outputDateTime(m.conditions.time.dateTime),
      sunStateOrigin.localSolarTime,
      this.outputSunState(sunStateOrigin),
    ]);
    html += this.outputLine([
      "Destination",
      m.destination_icao
        ? `<a target="airport" href="${SkyVector.airportLink(m.destination_icao)}">${Quote.html(
            m.destination_icao
          )}</a>`
        : "",
      m.destination_country,
      this.outputDateTime(time),
      sunStateDestination.localSolarTime,
      this.outputSunState(sunStateDestination),
    ]);

    this.elements.tbody.innerHTML = html;
  }
}

export class ComponentsCheckpoints extends ComponentsOutputtable {
  mission?: Mission;

  moreElements = {
    tfoot: <HTMLTableSectionElement>document.createElement("tfoot"),
    p: <HTMLParagraphElement>document.createElement("p"),
  };

  constructor() {
    super();
    // this.elements.table.appendChild(this.elements.caption);
    // this.elements.table.appendChild(this.elements.thead);
    // this.elements.table.appendChild(this.elements.tbody);
    // this.appendChild(this.elements.table);

    this.elements.table.appendChild(this.moreElements.tfoot);
    this.appendChild(this.moreElements.p);

    this.moreElements.p.className = "no-print";
    this.elements.caption.innerText = "Checkpoints";
    this.elements.thead.innerHTML = this.outputLine(
      [
        "#",
        "Waypoint ",
        '<abbr title="Frequency">FRQ</abbr>',
        "Altitude",
        '<abbr title="True Air Speed">TAS</abbr>',
        '<abbr title="Desired track magnetic">DTK</abbr>',
        '<abbr title="Heading magnetic">HDG</abbr> ',
        "Distance",
        '<abbr title="Estimated time enroute">ETE</abbr>',
      ],
      "th"
    );
    this.draw();
  }

  draw() {
    super.draw();

    const m = this.mission;
    if (!m || !this.mission || m.checkpoints.length === 0) {
      this.elements.table.classList.add("empty");
      this.moreElements.tfoot.innerHTML = "";
      this.moreElements.p.innerHTML = "";
      return;
    }

    const s = new SkyVector(m);
    const lonLatArea = new LonLatArea(this.mission.origin_lon_lat);
    this.mission.checkpoints.forEach((c) => {
      lonLatArea.push(c.lon_lat);
    });
    const zoomLevel = lonLatArea.getZoomLevel();
    const center = lonLatArea.center;

    {
      let html = "";

      m.checkpoints.forEach((c, i) => {
        const isAirport =
          c.type === MissionCheckpoint.TYPE_ORIGIN ||
          c.type === MissionCheckpoint.TYPE_DESTINATION ||
          i == 0 ||
          i == m.checkpoints.length - 1;
        const isAirportOrRunway =
          isAirport ||
          c.type === MissionCheckpoint.TYPE_DEPARTURE_RUNWAY ||
          c.type === MissionCheckpoint.TYPE_DESTINATION_RUNWAY;

        html += this.outputLine([
          Outputtable.pad(i + 1, 2, 0, "0") + ".",
          !isAirport
            ? `<input aria-label="Waypoint #${i + 1}" data-cp-id="${i}" data-cp-prop="name" type="text" value="${
                c.name
              }" pattern="[A-Z0-9._+\\-]+" maxlength="8" autocapitalize="characters" required="required" />`
            : c.name,
          `<input aria-label="Frequency #${
            i + 1
          }" data-cp-id="${i}" data-cp-prop="frequency_mhz" type="number" min="0.190" step="0.001" max="118" value="${
            c.frequency ? c.frequency_mhz : ""
          }" />&nbsp;MHz`,
          `<input aria-label="Altitude #${i + 1}" data-cp-id="${i}" data-cp-prop="altitude_ft" type="number" min="${
            !isAirportOrRunway ? -1000 : 0
          }" step="${!isAirportOrRunway ? 100 : 1}" value="${
            c.lon_lat.altitude_m ? Math.round(c.lon_lat.altitude_ft) : ""
          }" />&nbsp;ft`,
          i !== 0
            ? `<input aria-label="True Air Speed #${
                i + 1
              }" data-cp-id="${i}" data-cp-prop="speed" type="number" min="0" value="${
                c.speed >= 0 ? Math.round(c.speed) : ""
              }" />&nbsp;kts`
            : "",
          i !== 0 ? Outputtable.padThree(c.direction_magnetic) + "°" : "",
          i !== 0 ? '<span class="heading">' + Outputtable.padThree(c.heading_magnetic) + "</span>°" : "",
          i !== 0
            ? `<span class="distance" title="${c.slope_deg.toFixed(1)}°">${Outputtable.pad(
                c.distance,
                5,
                1
              )}</span>&nbsp;NM`
            : "",
          i !== 0
            ? '<span class="time_enroute">' + Outputtable.convertHoursToMinutesString(c.time_enroute) + "</span>"
            : "",
        ]);
      });
      this.elements.tbody.innerHTML = html;
    }

    {
      let html = "";
      html += this.outputLine(
        [
          "",
          "Total",
          "",
          "",
          "",
          "",
          "",
          Outputtable.pad(m.distance, 4, 1) + "&nbsp;NM",
          '<span class="time_enroute">' + Outputtable.convertHoursToMinutesString(m.time_enroute) + "</span>",
        ],
        "ttd"
      );
      this.moreElements.tfoot.innerHTML = html;
    }

    {
      let html = "";
      html += `<p class="no-print">Check your <a href="${s.toString(false)}" target="skyvector" title="${s
        .getCheckpoints(false)
        .join(" ")}">current flight plan on Sky Vector</a>.
    You may also want to take a look at <a href="https://www.google.com/maps/@?api=1&amp;map_action=map&amp;center=${encodeURIComponent(
      center.lat
    )},${encodeURIComponent(center.lon)}&amp;zoom=${encodeURIComponent(
        zoomLevel
      )}&amp;basemap=terrain" target="gmap">Google Maps</a> / <a href="https://www.openstreetmap.org/#map=${encodeURIComponent(
        zoomLevel
      )}/${encodeURIComponent(center.lat)}/${encodeURIComponent(center.lon)}" target="osm">OpenStreetMap</a>.</p>`;

      this.moreElements.p.innerHTML = html;
    }
  }
}
