import { getConfig } from '../../diagram-api/diagramAPI.js';
import { sanitizeText } from '../common/common.js';
import {
  setAccTitle,
  getAccTitle,
  getAccDescription,
  setAccDescription,
} from '../common/commonDb.js';

let c4ShapeArray = [];
let boundaryParseStack = [''];
let currentBoundaryParse = 'global';
let parentBoundaryParse = '';
let boundaries = [
  {
    alias: 'global',
    label: { text: 'global' },
    type: { text: 'global' },
    tags: null,
    link: null,
    parentBoundary: '',
  },
];
let rels = [];
let title = '';
let wrapEnabled = false;
let c4ShapeInRow = 4;
let c4BoundaryInRow = 2;
var c4Type;

export const getC4Type = function () {
  return c4Type;
};

export const setC4Type = function (c4TypeParam) {
  let sanitizedText = sanitizeText(c4TypeParam, getConfig());
  c4Type = sanitizedText;
};

//type, from, to, label, ?techn, ?descr, ?sprite, ?tags, $link
export const addRel = function (type, from, to, label, techn, descr, sprite, tags, link) {
  // Don't allow label nulling
  if (
    type === undefined ||
    type === null ||
    from === undefined ||
    from === null ||
    to === undefined ||
    to === null ||
    label === undefined ||
    label === null
  ) {
    return;
  }

  let rel = {};
  const old = rels.find((rel) => rel.from === from && rel.to === to);
  if (old) {
    rel = old;
  } else {
    rels.push(rel);
  }

  rel.type = type;
  rel.from = from;
  rel.to = to;
  rel.label = { text: label };

  if (techn === undefined || techn === null) {
    rel.techn = { text: '' };
  } else {
    if (typeof techn === 'object') {
      let [key, value] = Object.entries(techn)[0];
      rel[key] = { text: value };
    } else {
      rel.techn = { text: techn };
    }
  }

  if (descr === undefined || descr === null) {
    rel.descr = { text: '' };
  } else {
    if (typeof descr === 'object') {
      let [key, value] = Object.entries(descr)[0];
      rel[key] = { text: value };
    } else {
      rel.descr = { text: descr };
    }
  }

  if (typeof sprite === 'object') {
    let [key, value] = Object.entries(sprite)[0];
    rel[key] = value;
  } else {
    rel.sprite = sprite;
  }
  if (typeof tags === 'object') {
    let [key, value] = Object.entries(tags)[0];
    rel[key] = value;
  } else {
    rel.tags = tags;
  }
  if (typeof link === 'object') {
    let [key, value] = Object.entries(link)[0];
    rel[key] = value;
  } else {
    rel.link = link;
  }
  rel.wrap = autoWrap();
};

//type, alias, label, ?descr, ?sprite, ?tags, $link
export const addPersonOrSystem = function (typeC4Shape, alias, label, descr, sprite, tags, link) {
  // Don't allow label nulling
  if (alias === null || label === null) {
    return;
  }

  let personOrSystem = {};
  const old = c4ShapeArray.find((personOrSystem) => personOrSystem.alias === alias);
  if (old && alias === old.alias) {
    personOrSystem = old;
  } else {
    personOrSystem.alias = alias;
    c4ShapeArray.push(personOrSystem);
  }

  // Don't allow null labels, either
  if (label === undefined || label === null) {
    personOrSystem.label = { text: '' };
  } else {
    personOrSystem.label = { text: label };
  }

  if (descr === undefined || descr === null) {
    personOrSystem.descr = { text: '' };
  } else {
    if (typeof descr === 'object') {
      let [key, value] = Object.entries(descr)[0];
      personOrSystem[key] = { text: value };
    } else {
      personOrSystem.descr = { text: descr };
    }
  }

  if (typeof sprite === 'object') {
    let [key, value] = Object.entries(sprite)[0];
    personOrSystem[key] = value;
  } else {
    personOrSystem.sprite = sprite;
  }
  if (typeof tags === 'object') {
    let [key, value] = Object.entries(tags)[0];
    personOrSystem[key] = value;
  } else {
    personOrSystem.tags = tags;
  }
  if (typeof link === 'object') {
    let [key, value] = Object.entries(link)[0];
    personOrSystem[key] = value;
  } else {
    personOrSystem.link = link;
  }
  personOrSystem.typeC4Shape = { text: typeC4Shape };
  personOrSystem.parentBoundary = currentBoundaryParse;
  personOrSystem.wrap = autoWrap();
};

//type, alias, label, ?techn, ?descr ?sprite, ?tags, $link
export const addContainer = function (typeC4Shape, alias, label, techn, descr, sprite, tags, link) {
  // Don't allow label nulling
  if (alias === null || label === null) {
    return;
  }

  let container = {};
  const old = c4ShapeArray.find((container) => container.alias === alias);
  if (old && alias === old.alias) {
    container = old;
  } else {
    container.alias = alias;
    c4ShapeArray.push(container);
  }

  // Don't allow null labels, either
  if (label === undefined || label === null) {
    container.label = { text: '' };
  } else {
    container.label = { text: label };
  }

  if (techn === undefined || techn === null) {
    container.techn = { text: '' };
  } else {
    if (typeof techn === 'object') {
      let [key, value] = Object.entries(techn)[0];
      container[key] = { text: value };
    } else {
      container.techn = { text: techn };
    }
  }

  if (descr === undefined || descr === null) {
    container.descr = { text: '' };
  } else {
    if (typeof descr === 'object') {
      let [key, value] = Object.entries(descr)[0];
      container[key] = { text: value };
    } else {
      container.descr = { text: descr };
    }
  }

  if (typeof sprite === 'object') {
    let [key, value] = Object.entries(sprite)[0];
    container[key] = value;
  } else {
    container.sprite = sprite;
  }
  if (typeof tags === 'object') {
    let [key, value] = Object.entries(tags)[0];
    container[key] = value;
  } else {
    container.tags = tags;
  }
  if (typeof link === 'object') {
    let [key, value] = Object.entries(link)[0];
    container[key] = value;
  } else {
    container.link = link;
  }
  container.wrap = autoWrap();
  container.typeC4Shape = { text: typeC4Shape };
  container.parentBoundary = currentBoundaryParse;
};

//type, alias, label, ?techn, ?descr ?sprite, ?tags, $link
export const addComponent = function (typeC4Shape, alias, label, techn, descr, sprite, tags, link) {
  // Don't allow label nulling
  if (alias === null || label === null) {
    return;
  }

  let component = {};
  const old = c4ShapeArray.find((component) => component.alias === alias);
  if (old && alias === old.alias) {
    component = old;
  } else {
    component.alias = alias;
    c4ShapeArray.push(component);
  }

  // Don't allow null labels, either
  if (label === undefined || label === null) {
    component.label = { text: '' };
  } else {
    component.label = { text: label };
  }

  if (techn === undefined || techn === null) {
    component.techn = { text: '' };
  } else {
    if (typeof techn === 'object') {
      let [key, value] = Object.entries(techn)[0];
      component[key] = { text: value };
    } else {
      component.techn = { text: techn };
    }
  }

  if (descr === undefined || descr === null) {
    component.descr = { text: '' };
  } else {
    if (typeof descr === 'object') {
      let [key, value] = Object.entries(descr)[0];
      component[key] = { text: value };
    } else {
      component.descr = { text: descr };
    }
  }

  if (typeof sprite === 'object') {
    let [key, value] = Object.entries(sprite)[0];
    component[key] = value;
  } else {
    component.sprite = sprite;
  }
  if (typeof tags === 'object') {
    let [key, value] = Object.entries(tags)[0];
    component[key] = value;
  } else {
    component.tags = tags;
  }
  if (typeof link === 'object') {
    let [key, value] = Object.entries(link)[0];
    component[key] = value;
  } else {
    component.link = link;
  }
  component.wrap = autoWrap();
  component.typeC4Shape = { text: typeC4Shape };
  component.parentBoundary = currentBoundaryParse;
};

//alias, label, ?type, ?tags, $link
export const addPersonOrSystemBoundary = function (alias, label, type, tags, link) {
  // if (parentBoundary === null) return;

  // Don't allow label nulling
  if (alias === null || label === null) {
    return;
  }

  let boundary = {};
  const old = boundaries.find((boundary) => boundary.alias === alias);
  if (old && alias === old.alias) {
    boundary = old;
  } else {
    boundary.alias = alias;
    boundaries.push(boundary);
  }

  // Don't allow null labels, either
  if (label === undefined || label === null) {
    boundary.label = { text: '' };
  } else {
    boundary.label = { text: label };
  }

  if (type === undefined || type === null) {
    boundary.type = { text: 'system' };
  } else {
    if (typeof type === 'object') {
      let [key, value] = Object.entries(type)[0];
      boundary[key] = { text: value };
    } else {
      boundary.type = { text: type };
    }
  }

  if (typeof tags === 'object') {
    let [key, value] = Object.entries(tags)[0];
    boundary[key] = value;
  } else {
    boundary.tags = tags;
  }
  if (typeof link === 'object') {
    let [key, value] = Object.entries(link)[0];
    boundary[key] = value;
  } else {
    boundary.link = link;
  }
  boundary.parentBoundary = currentBoundaryParse;
  boundary.wrap = autoWrap();

  parentBoundaryParse = currentBoundaryParse;
  currentBoundaryParse = alias;
  boundaryParseStack.push(parentBoundaryParse);
};

//alias, label, ?type, ?tags, $link
export const addContainerBoundary = function (alias, label, type, tags, link) {
  // if (parentBoundary === null) return;

  // Don't allow label nulling
  if (alias === null || label === null) {
    return;
  }

  let boundary = {};
  const old = boundaries.find((boundary) => boundary.alias === alias);
  if (old && alias === old.alias) {
    boundary = old;
  } else {
    boundary.alias = alias;
    boundaries.push(boundary);
  }

  // Don't allow null labels, either
  if (label === undefined || label === null) {
    boundary.label = { text: '' };
  } else {
    boundary.label = { text: label };
  }

  if (type === undefined || type === null) {
    boundary.type = { text: 'container' };
  } else {
    if (typeof type === 'object') {
      let [key, value] = Object.entries(type)[0];
      boundary[key] = { text: value };
    } else {
      boundary.type = { text: type };
    }
  }

  if (typeof tags === 'object') {
    let [key, value] = Object.entries(tags)[0];
    boundary[key] = value;
  } else {
    boundary.tags = tags;
  }
  if (typeof link === 'object') {
    let [key, value] = Object.entries(link)[0];
    boundary[key] = value;
  } else {
    boundary.link = link;
  }
  boundary.parentBoundary = currentBoundaryParse;
  boundary.wrap = autoWrap();

  parentBoundaryParse = currentBoundaryParse;
  currentBoundaryParse = alias;
  boundaryParseStack.push(parentBoundaryParse);
};

//alias, label, ?type, ?descr, ?sprite, ?tags, $link
export const addDeploymentNode = function (
  nodeType,
  alias,
  label,
  type,
  descr,
  sprite,
  tags,
  link
) {
  // if (parentBoundary === null) return;

  // Don't allow label nulling
  if (alias === null || label === null) {
    return;
  }

  let boundary = {};
  const old = boundaries.find((boundary) => boundary.alias === alias);
  if (old && alias === old.alias) {
    boundary = old;
  } else {
    boundary.alias = alias;
    boundaries.push(boundary);
  }

  // Don't allow null labels, either
  if (label === undefined || label === null) {
    boundary.label = { text: '' };
  } else {
    boundary.label = { text: label };
  }

  if (type === undefined || type === null) {
    boundary.type = { text: 'node' };
  } else {
    if (typeof type === 'object') {
      let [key, value] = Object.entries(type)[0];
      boundary[key] = { text: value };
    } else {
      boundary.type = { text: type };
    }
  }

  if (descr === undefined || descr === null) {
    boundary.descr = { text: '' };
  } else {
    if (typeof descr === 'object') {
      let [key, value] = Object.entries(descr)[0];
      boundary[key] = { text: value };
    } else {
      boundary.descr = { text: descr };
    }
  }

  if (typeof tags === 'object') {
    let [key, value] = Object.entries(tags)[0];
    boundary[key] = value;
  } else {
    boundary.tags = tags;
  }
  if (typeof link === 'object') {
    let [key, value] = Object.entries(link)[0];
    boundary[key] = value;
  } else {
    boundary.link = link;
  }
  boundary.nodeType = nodeType;
  boundary.parentBoundary = currentBoundaryParse;
  boundary.wrap = autoWrap();

  parentBoundaryParse = currentBoundaryParse;
  currentBoundaryParse = alias;
  boundaryParseStack.push(parentBoundaryParse);
};

export const popBoundaryParseStack = function () {
  currentBoundaryParse = parentBoundaryParse;
  boundaryParseStack.pop();
  parentBoundaryParse = boundaryParseStack.pop();
  boundaryParseStack.push(parentBoundaryParse);
};

//elementName, ?bgColor, ?fontColor, ?borderColor, ?shadowing, ?shape, ?sprite, ?techn, ?legendText, ?legendSprite
export const updateElStyle = function (
  typeC4Shape,
  elementName,
  bgColor,
  fontColor,
  borderColor,
  shadowing,
  shape,
  sprite,
  techn,
  legendText,
  legendSprite
) {
  let old = c4ShapeArray.find((element) => element.alias === elementName);
  if (old === undefined) {
    old = boundaries.find((element) => element.alias === elementName);
    if (old === undefined) {
      return;
    }
  }
  if (bgColor !== undefined && bgColor !== null) {
    if (typeof bgColor === 'object') {
      let [key, value] = Object.entries(bgColor)[0];
      old[key] = value;
    } else {
      old.bgColor = bgColor;
    }
  }
  if (fontColor !== undefined && fontColor !== null) {
    if (typeof fontColor === 'object') {
      let [key, value] = Object.entries(fontColor)[0];
      old[key] = value;
    } else {
      old.fontColor = fontColor;
    }
  }
  if (borderColor !== undefined && borderColor !== null) {
    if (typeof borderColor === 'object') {
      let [key, value] = Object.entries(borderColor)[0];
      old[key] = value;
    } else {
      old.borderColor = borderColor;
    }
  }
  if (shadowing !== undefined && shadowing !== null) {
    if (typeof shadowing === 'object') {
      let [key, value] = Object.entries(shadowing)[0];
      old[key] = value;
    } else {
      old.shadowing = shadowing;
    }
  }
  if (shape !== undefined && shape !== null) {
    if (typeof shape === 'object') {
      let [key, value] = Object.entries(shape)[0];
      old[key] = value;
    } else {
      old.shape = shape;
    }
  }
  if (sprite !== undefined && sprite !== null) {
    if (typeof sprite === 'object') {
      let [key, value] = Object.entries(sprite)[0];
      old[key] = value;
    } else {
      old.sprite = sprite;
    }
  }
  if (techn !== undefined && techn !== null) {
    if (typeof techn === 'object') {
      let [key, value] = Object.entries(techn)[0];
      old[key] = value;
    } else {
      old.techn = techn;
    }
  }
  if (legendText !== undefined && legendText !== null) {
    if (typeof legendText === 'object') {
      let [key, value] = Object.entries(legendText)[0];
      old[key] = value;
    } else {
      old.legendText = legendText;
    }
  }
  if (legendSprite !== undefined && legendSprite !== null) {
    if (typeof legendSprite === 'object') {
      let [key, value] = Object.entries(legendSprite)[0];
      old[key] = value;
    } else {
      old.legendSprite = legendSprite;
    }
  }
};

//textColor, lineColor, ?offsetX, ?offsetY
export const updateRelStyle = function (
  typeC4Shape,
  from,
  to,
  textColor,
  lineColor,
  offsetX,
  offsetY
) {
  const old = rels.find((rel) => rel.from === from && rel.to === to);
  if (old === undefined) {
    return;
  }
  if (textColor !== undefined && textColor !== null) {
    if (typeof textColor === 'object') {
      let [key, value] = Object.entries(textColor)[0];
      old[key] = value;
    } else {
      old.textColor = textColor;
    }
  }
  if (lineColor !== undefined && lineColor !== null) {
    if (typeof lineColor === 'object') {
      let [key, value] = Object.entries(lineColor)[0];
      old[key] = value;
    } else {
      old.lineColor = lineColor;
    }
  }
  if (offsetX !== undefined && offsetX !== null) {
    if (typeof offsetX === 'object') {
      let [key, value] = Object.entries(offsetX)[0];
      old[key] = parseInt(value);
    } else {
      old.offsetX = parseInt(offsetX);
    }
  }
  if (offsetY !== undefined && offsetY !== null) {
    if (typeof offsetY === 'object') {
      let [key, value] = Object.entries(offsetY)[0];
      old[key] = parseInt(value);
    } else {
      old.offsetY = parseInt(offsetY);
    }
  }
};

//?c4ShapeInRow, ?c4BoundaryInRow
export const updateLayoutConfig = function (typeC4Shape, c4ShapeInRowParam, c4BoundaryInRowParam) {
  let c4ShapeInRowValue = c4ShapeInRow;
  let c4BoundaryInRowValue = c4BoundaryInRow;

  if (typeof c4ShapeInRowParam === 'object') {
    const value = Object.values(c4ShapeInRowParam)[0];
    c4ShapeInRowValue = parseInt(value);
  } else {
    c4ShapeInRowValue = parseInt(c4ShapeInRowParam);
  }
  if (typeof c4BoundaryInRowParam === 'object') {
    const value = Object.values(c4BoundaryInRowParam)[0];
    c4BoundaryInRowValue = parseInt(value);
  } else {
    c4BoundaryInRowValue = parseInt(c4BoundaryInRowParam);
  }

  if (c4ShapeInRowValue >= 1) {
    c4ShapeInRow = c4ShapeInRowValue;
  }
  if (c4BoundaryInRowValue >= 1) {
    c4BoundaryInRow = c4BoundaryInRowValue;
  }
};

export const getC4ShapeInRow = function () {
  return c4ShapeInRow;
};
export const getC4BoundaryInRow = function () {
  return c4BoundaryInRow;
};
export const getCurrentBoundaryParse = function () {
  return currentBoundaryParse;
};

export const getParentBoundaryParse = function () {
  return parentBoundaryParse;
};

export const getC4ShapeArray = function (parentBoundary) {
  if (parentBoundary === undefined || parentBoundary === null) {
    return c4ShapeArray;
  } else {
    return c4ShapeArray.filter((personOrSystem) => {
      return personOrSystem.parentBoundary === parentBoundary;
    });
  }
};
export const getC4Shape = function (alias) {
  return c4ShapeArray.find((personOrSystem) => personOrSystem.alias === alias);
};
export const getC4ShapeKeys = function (parentBoundary) {
  return Object.keys(getC4ShapeArray(parentBoundary));
};

export const getBoundaries = function (parentBoundary) {
  if (parentBoundary === undefined || parentBoundary === null) {
    return boundaries;
  } else {
    return boundaries.filter((boundary) => boundary.parentBoundary === parentBoundary);
  }
};

/**
 * @deprecated Use {@link getBoundaries} instead
 */
export const getBoundarys = getBoundaries;

export const getRels = function () {
  return rels;
};

export const getTitle = function () {
  return title;
};

export const setWrap = function (wrapSetting) {
  wrapEnabled = wrapSetting;
};

export const autoWrap = function () {
  return wrapEnabled;
};

export const clear = function () {
  c4ShapeArray = [];
  boundaries = [
    {
      alias: 'global',
      label: { text: 'global' },
      type: { text: 'global' },
      tags: null,
      link: null,
      parentBoundary: '',
    },
  ];
  parentBoundaryParse = '';
  currentBoundaryParse = 'global';
  boundaryParseStack = [''];
  rels = [];

  boundaryParseStack = [''];
  title = '';
  wrapEnabled = false;
  c4ShapeInRow = 4;
  c4BoundaryInRow = 2;
};

export const LINETYPE = {
  SOLID: 0,
  DOTTED: 1,
  NOTE: 2,
  SOLID_CROSS: 3,
  DOTTED_CROSS: 4,
  SOLID_OPEN: 5,
  DOTTED_OPEN: 6,
  LOOP_START: 10,
  LOOP_END: 11,
  ALT_START: 12,
  ALT_ELSE: 13,
  ALT_END: 14,
  OPT_START: 15,
  OPT_END: 16,
  ACTIVE_START: 17,
  ACTIVE_END: 18,
  PAR_START: 19,
  PAR_AND: 20,
  PAR_END: 21,
  RECT_START: 22,
  RECT_END: 23,
  SOLID_POINT: 24,
  DOTTED_POINT: 25,
};

export const ARROWTYPE = {
  FILLED: 0,
  OPEN: 1,
};

export const PLACEMENT = {
  LEFTOF: 0,
  RIGHTOF: 1,
  OVER: 2,
};

export const setTitle = function (txt) {
  let sanitizedText = sanitizeText(txt, getConfig());
  title = sanitizedText;
};

export default {
  addPersonOrSystem,
  addPersonOrSystemBoundary,
  addContainer,
  addContainerBoundary,
  addComponent,
  addDeploymentNode,
  popBoundaryParseStack,
  addRel,
  updateElStyle,
  updateRelStyle,
  updateLayoutConfig,
  autoWrap,
  setWrap,
  getC4ShapeArray,
  getC4Shape,
  getC4ShapeKeys,
  getBoundaries,
  getBoundarys,
  getCurrentBoundaryParse,
  getParentBoundaryParse,
  getRels,
  getTitle,
  getC4Type,
  getC4ShapeInRow,
  getC4BoundaryInRow,
  setAccTitle,
  getAccTitle,
  getAccDescription,
  setAccDescription,
  getConfig: () => getConfig().c4,
  clear,
  LINETYPE,
  ARROWTYPE,
  PLACEMENT,
  setTitle,
  setC4Type,
  // apply,
};
