import { Paragraph } from "./paragraph";
import { Table, convertWidthType } from "./table";
import { Shading } from "./shading";
import { TableCellBorders, PositionKeys } from "./table-cell-borders";
import { TableCellBorderPosition, TableCellBorder } from "./table-cell-border";
import * as wasm from "./pkg";
import { convertBorderType } from "./run-property";
import { TableOfContents } from "./table-of-contents";
import { build } from "./builder";
import { WidthType } from ".";

export type VMergeType = "restart" | "continue";

export type VAlignType = "top" | "center" | "bottom";

export type TextDirectionType =
  | "lr"
  | "lrV"
  | "rl"
  | "rlV"
  | "tb"
  | "tbV"
  | "tbRlV"
  | "tbRl"
  | "btLr"
  | "lrTbV";

export const toTextDirectionWasmType = (
  t: TextDirectionType
): wasm.TextDirectionType => {
  switch (t) {
    case "lr":
      return wasm.TextDirectionType.Lr;
    case "lrV":
      return wasm.TextDirectionType.LrV;
    case "rl":
      return wasm.TextDirectionType.Rl;
    case "rlV":
      return wasm.TextDirectionType.RlV;
    case "tb":
      return wasm.TextDirectionType.Tb;
    case "tbV":
      return wasm.TextDirectionType.TbV;
    case "tbRlV":
      return wasm.TextDirectionType.TbRlV;
    case "tbRl":
      return wasm.TextDirectionType.TbRl;
    case "btLr":
      return wasm.TextDirectionType.BtLr;
    case "lrTbV":
      return wasm.TextDirectionType.LrTbV;
    default:
      throw new Error("unreachable");
  }
};

export type CellProperty = {
  borders: TableCellBorders;
  verticalMerge?: VMergeType;
  verticalAlign?: VAlignType;
  gridSpan?: number;
  width?: number;
  textDirection?: TextDirectionType;
  shading?: Shading;
  margins?: {
    top?: { val: number; type: WidthType };
    left?: { val: number; type: WidthType };
    bottom?: { val: number; type: WidthType };
    right?: { val: number; type: WidthType };
  };
};

export class TableCell {
  children: (Paragraph | Table | TableOfContents)[] = [];
  hasNumberings = false;
  property: CellProperty = {
    borders: new TableCellBorders(),
  };

  addParagraph(p: Paragraph) {
    if (p.hasNumberings) {
      this.hasNumberings = true;
    }
    this.children.push(p);
    return this;
  }

  addTableOfContents(t: TableOfContents) {
    this.children.push(t);
    return this;
  }

  addTable(t: Table) {
    if (t.hasNumberings) {
      this.hasNumberings = true;
    }
    this.children.push(t);
    return this;
  }

  verticalMerge(t: VMergeType) {
    this.property.verticalMerge = t;
    return this;
  }

  verticalAlign(t: VAlignType) {
    this.property.verticalAlign = t;
    return this;
  }

  gridSpan(v: number) {
    this.property.gridSpan = v;
    return this;
  }

  width(v: number) {
    this.property.width = v;
    return this;
  }

  shading(type: string, color: string, fill: string) {
    const s = new Shading();
    s.color(color);
    s.fill(fill);
    s.type(type);
    this.property.shading = s;
    return this;
  }

  textDirection(t: TextDirectionType) {
    this.property.textDirection = t;
    return this;
  }

  setBorder(border: TableCellBorder) {
    this.property.borders[border.position.toLowerCase() as PositionKeys] =
      border;
    return this;
  }

  clearBorder(position: TableCellBorderPosition) {
    this.property.borders[position.toLowerCase() as PositionKeys] =
      new TableCellBorder(position).border_type("nil");
    return this;
  }

  marginTop(v: number, t: WidthType) {
    this.property.margins = {
      ...this.property.margins,
      top: { val: v, type: t },
    };
    return this;
  }

  marginLeft(v: number, t: WidthType) {
    this.property.margins = {
      ...this.property.margins,
      left: { val: v, type: t },
    };
    return this;
  }

  marginRight(v: number, t: WidthType) {
    this.property.margins = {
      ...this.property.margins,
      right: { val: v, type: t },
    };
    return this;
  }

  marginBottom(v: number, t: WidthType) {
    this.property.margins = {
      ...this.property.margins,
      bottom: { val: v, type: t },
    };
    return this;
  }

  buildCellBorders(cell: wasm.TableCell): wasm.TableCell {
    if (this.property.borders.top) {
      const border = wasm
        .createTableCellBorder(wasm.TableCellBorderPosition.Top)
        .size(this.property.borders.top._size)
        .color(this.property.borders.top._color)
        .border_type(convertBorderType(this.property.borders.top._border_type));
      cell = cell.set_border(border);
    }

    if (this.property.borders.right) {
      const border = wasm
        .createTableCellBorder(wasm.TableCellBorderPosition.Right)
        .size(this.property.borders.right._size)
        .color(this.property.borders.right._color)
        .border_type(
          convertBorderType(this.property.borders.right._border_type)
        );
      cell = cell.set_border(border);
    }

    if (this.property.borders.bottom) {
      const border = wasm
        .createTableCellBorder(wasm.TableCellBorderPosition.Bottom)
        .size(this.property.borders.bottom._size)
        .color(this.property.borders.bottom._color)
        .border_type(
          convertBorderType(this.property.borders.bottom._border_type)
        );
      cell = cell.set_border(border);
    }

    if (this.property.borders.left) {
      const border = wasm
        .createTableCellBorder(wasm.TableCellBorderPosition.Left)
        .size(this.property.borders.left._size)
        .color(this.property.borders.left._color)
        .border_type(
          convertBorderType(this.property.borders.left._border_type)
        );
      cell = cell.set_border(border);
    }

    if (this.property.borders.insideH) {
      const border = wasm
        .createTableCellBorder(wasm.TableCellBorderPosition.InsideH)
        .size(this.property.borders.insideH._size)
        .color(this.property.borders.insideH._color)
        .border_type(
          convertBorderType(this.property.borders.insideH._border_type)
        );
      cell = cell.set_border(border);
    }

    if (this.property.borders.insideV) {
      const border = wasm
        .createTableCellBorder(wasm.TableCellBorderPosition.InsideV)
        .size(this.property.borders.insideV._size)
        .color(this.property.borders.insideV._color)
        .border_type(
          convertBorderType(this.property.borders.insideV._border_type)
        );
      cell = cell.set_border(border);
    }

    if (this.property.borders.tl2br) {
      const border = wasm
        .createTableCellBorder(wasm.TableCellBorderPosition.Tl2br)
        .size(this.property.borders.tl2br._size)
        .color(this.property.borders.tl2br._color)
        .border_type(
          convertBorderType(this.property.borders.tl2br._border_type)
        );
      cell = cell.set_border(border);
    }

    if (this.property.borders.tr2bl) {
      const border = wasm
        .createTableCellBorder(wasm.TableCellBorderPosition.Tr2bl)
        .size(this.property.borders.tr2bl._size)
        .color(this.property.borders.tr2bl._color)
        .border_type(
          convertBorderType(this.property.borders.tr2bl._border_type)
        );
      cell = cell.set_border(border);
    }

    if (this.property.margins?.top) {
      cell = cell.margin_top(
        this.property.margins?.top.val,
        convertWidthType(this.property.margins?.top.type)
      );
    }

    if (this.property.margins?.right) {
      cell = cell.margin_right(
        this.property.margins?.right.val,
        convertWidthType(this.property.margins?.right.type)
      );
    }

    if (this.property.margins?.bottom) {
      cell = cell.margin_bottom(
        this.property.margins?.bottom.val,
        convertWidthType(this.property.margins?.bottom.type)
      );
    }

    if (this.property.margins?.left) {
      cell = cell.margin_left(
        this.property.margins?.left.val,
        convertWidthType(this.property.margins?.left.type)
      );
    }

    return cell;
  }

  build() {
    let cell = wasm.createTableCell();
    this.children.forEach((c) => {
      if (c instanceof Paragraph) {
        cell = cell.add_paragraph(build(c));
      } else if (c instanceof Table) {
        const table = c.build();
        cell = cell.add_table(table);
      } else if (c instanceof TableOfContents) {
        cell = cell.add_table_of_contents(c.buildWasmObject());
      } else {
        // eslint-disable-next-line
        const _: never = c;
        console.error(_);
      }
    });

    if (this.property.verticalMerge === "continue") {
      cell = cell.vertical_merge(wasm.VMergeType.Continue);
    } else if (this.property.verticalMerge === "restart") {
      cell = cell.vertical_merge(wasm.VMergeType.Restart);
    }

    switch (this.property.verticalAlign) {
      case "top": {
        cell = cell.vertical_align(wasm.VAlignType.Top);
        break;
      }
      case "center": {
        cell = cell.vertical_align(wasm.VAlignType.Center);
        break;
      }
      case "bottom": {
        cell = cell.vertical_align(wasm.VAlignType.Bottom);
        break;
      }
    }

    if (typeof this.property.gridSpan !== "undefined") {
      cell = cell.grid_span(this.property.gridSpan);
    }

    if (typeof this.property.width !== "undefined") {
      cell = cell.width(this.property.width);
    }

    if (typeof this.property.textDirection !== "undefined") {
      cell = cell.text_direction(
        toTextDirectionWasmType(this.property.textDirection)
      );
    }

    if (typeof this.property.borders !== "undefined") {
      cell = this.buildCellBorders(cell);
    }

    if (typeof this.property.shading !== "undefined") {
      cell = cell.shading(
        this.property.shading._type,
        this.property.shading._color,
        this.property.shading._fill
      );
    }

    return cell;
  }
}
