// Рекурсия: HTML tree
// Используя решение этого задания сделайте функцию, которая рекурсивно создает HTML-строку из древовидной
// структуры данных Javascript любой вложенности. Проверьте результат работы функции выводя HTML-строку
// используя document.write или же какой-то_элемент.innerHTML
html_tree: {

  function htmlTree(element) {
    let str = '';
    let attrStr = '';
    if (element.attrs) {
      Object.keys(element.attrs).forEach(key => {
        attrStr += `${key}=${element.attrs[key]} `
      });
    }
    str += `<${element.tagName} ${attrStr}>`
    if (element.children) {
      for (let childIndex in Object.keys(element.children)) {
        if (element.children[childIndex].tagName) {
          str += htmlTree(element.children[childIndex]);
        } else {
          str += `${element.children}`;
        }
      }
    }
    str += `</${element.tagName}>`;
    return str;
  }
  const table = {
    tagName: 'table',
    attrs: {
      border: "1",
    },
    children: [
      {
        tagName: 'tr',
        children: [
          {
            tagName: "td",
            children: ["1x1"],
          },
          {
            tagName: "td",
            children: ["1x2"],
          },
        ]
      },
      {
        tagName: 'tr',
        children: [
          {
            tagName: "td",
            children: ["2x1"],
          },
          {
            tagName: "td",
            children: ["2x2"],
          },
        ]
      }
    ]
  }
  document.write(htmlTree(table));
}

// Рекурсия: DOM tree
// Используя решение этого задания сделайте функцию, которая рекурсивно создает DOM из древовидной структуры
// данных Javascript. Задание в целом аналогично предыдущему, однако вместо накопления результата в HTML-строке
// функция должна добавлять элементы созданные через document.createElement в переданного в функцию родителя.
dom_tree: {
  function domTree(parent, element) {
    let el = document.createElement(element.tagName);
    if (element.attrs) {
      Object.keys(element.attrs).forEach(key => {
        el[key] = element.attrs[key];
      });
    }
    parent.append(el);
    if (element.children) {
      for (let childIndex in Object.keys(element.children)) {
        if (element.children[childIndex].tagName) {
          domTree(el, element.children[childIndex]);
        } else {
          el.innerText = element.children;
        }
      }
    }
  }
  const table = {
    tagName: 'table',
    attrs: {
      border: "1",
    },
    children: [
      {
        tagName: 'tr',
        children: [
          {
            tagName: "td",
            children: ["1x1"],
          },
          {
            tagName: "td",
            children: ["1x2"],
          },
        ]
      },
      {
        tagName: 'tr',
        children: [
          {
            tagName: "td",
            children: ["2x1"],
          },
          {
            tagName: "td",
            children: ["2x2"],
          },
        ]
      }
    ]
  }
  domTree(document.body, table);
}

// Рекурсия: Deep Copy
// Напишите функцию, которая рекурсивно осуществляет глубокое копирование структур Javascript, в которых
// нет циклических связей.
deep_copy: {
  const table = {
    tagName: 'table',
    attrs: {
      border: "1",
    },
    children: [
      {
        tagName: 'tr',
        children: [
          {
            tagName: "td",
            children: ["1x1"],
          },
          {
            tagName: "td",
            children: ["1x2"],
          },
        ]
      },
      {
        tagName: 'tr',
        children: [
          {
            tagName: "td",
            children: ["2x1"],
          },
          {
            tagName: "td",
            children: ["2x2"],
          },
        ]
      }
    ]
  }
  const arr = [1, "string", null, undefined, { a: 15, b: 10, c: [1, 2, 3, 4], d: undefined, e: true }, true, false]

  function deepCopy(obj) {
    let copy;
    if (Array.isArray(obj)) {
      copy = [];
    } else {
      copy = {};
    }
    if (obj) {
      Object.keys(obj).forEach(key => {
        if (typeof obj[key] == 'object') {
          copy[key] = deepCopy(obj[key]);
        } else {
          copy[key] = obj[key];
        }
      });
    }

    return copy;

  }
  const arr2 = deepCopy(arr); //arr2 и все его вложенные массивы и объекты - другие объекты, которые можно менять без риска поменять что-то в arr
  console.log(arr2);
  const table2 = deepCopy(table); //аналогично
  console.log(table2);
}

// Рекурсия: My Stringify
// Напишите аналог JSON.stringify
my_stringify: {
  function stringify(obj) {
    let str = '';
    let length = Array.isArray(obj) ? obj.length : Object.keys(obj).length;
    if (typeof obj == 'object') {
      str += Array.isArray(obj) ? '[' : '{';
    }
    Object.keys(obj).forEach((key, i) => {
      if (!Array.isArray(obj)) {
        str += `"${key}":`;
      }
      if (typeof obj[key] == 'object' && obj[key] != null) {
        str += stringify(obj[key]);
      } else if (typeof obj[key] == 'string') {
        str += `"${obj[key]}"`;
      } else if (obj[key] == undefined) {
        str += `null`;
      } else {
        str += `${obj[key]}`;
      }
      if (i != length - 1) {
        str += ',';
      }
    });
    if (typeof obj == 'object') {
      str += Array.isArray(obj) ? ']' : '}';
    }
    return str;
  }

  const arr = [1, "string", null, undefined, { a: 15, b: 10, c: [1, 2, 3, 4], d: undefined, e: true }, true, false];
  const jsonString = stringify(arr); //напишите функцию stringify без использования JSON.stringify
  console.log(jsonString);
  console.log(JSON.parse(jsonString));//не должно поломаться и должно вернуть структуру, во всем схожую с оригинальным arr или table
}

// Рекурсия: getElementById throw
// Напишите функцию getElementById, которая будет аналогична document.getElementById. В качестве основы можете
// взять материал лекции (walker), однако в цикл перебора children вставьте проверку на нахождение
// переданного id. При нахождении элемента по id в рекурсивной функции выбрасывайте исключение со значением
// найденного DOM-элемента, которое будет поймано на уровне вашей функции getElementById, после чего она
// вернет выброшенный DOM-элемент.
get_element_by_id: {
  function getElementById(idToFind) {
    function walker(parent = document.body) {
      for (const child of parent.children) {
        if (child.id == idToFind) {
          throw child;
        }
        walker(child);
      }
    }
    try {
      walker();
    } catch (e) {
      return e;
    }
  }
  console.log(getElementById('wantedParagraph'));
}