<?php

namespace Snaju\Inception\Util\Loaders\ArrayList;

use Snaju\Inception\Exception\Exception;

class ArrayList
{

    private ArrayListData $root;

    private ArrayListData $cursor;

    /**
     * ArrayList constructor.
     */
    public function __construct()
    {
        $this->root = new ArrayListData(null, null, null,null);
        $this->cursor = $this->root;
    }

    public function add(string $key, $data)
    {
        if ($this->containsKey($key)) {
            throw new Exception("Key already set in ArrayList");
        }

        $this->cursor->setNext(new ArrayListData($this->cursor, null, $key, $data));
        $this->next();
    }

    public function addToFront(string $key, $data)
    {
        $this->cursor = $this->root;
        $this->cursor->setNext(new ArrayListData($this->root, $this->cursor->getNext(), $key, $data));
        $this->next();
    }

    public function addToEnd(string $key, $data)
    {
        $this->end();
        $this->add($key, $data);
    }

    public function reset()
    {
        $this->cursor = $this->root;
    }

    public function end()
    {
        while ($this->next()) {
            // Do Nothing.
        }
    }

    public function clear()
    {
        $this->root = new ArrayListData(null, null, null, null);
        $this->cursor = $this->root;
    }

    public function next(): bool
    {
        if ($this->cursor->getNext() != null) {
            $this->cursor = $this->cursor->getNext();
            return true;
        }

        return false;
    }

    public function prev(): bool
    {
        $this->cursor = $this->cursor->getPrevious();
        if ($this->cursor == null) {
            $this->cursor = $this->root;
            return false;
        }

        return true;
    }

    public function remove(string $key): bool
    {
        $this->cursor = $this->root;

        while ($this->next()) {
            // While we have a next.
            if (strtolower($this->cursor->getKey()) == strtolower($key)) {
                // Remove from list.
                $this->cursor->getPrevious()->setNext($this->cursor->getNext());
                return true;
            }
        }

        return false;
    }

    public function containsKey(string $key)
    {
        $tmpList = clone $this->root;
        $cursor = $tmpList;

        while ($cursor->getNext() != null) {
            if (strtolower($key) == strtolower($cursor->getKey())) {
                return true;
            }
        }

        return false;
    }

    public function toArray(): array
    {
        $arr = [];
        $this->reset();
        while ($this->next()) {
            if ($this->cursor->getData() instanceof ArrayList) {
                $arr[$this->cursor->getKey()] = $this->cursor->getData()->toArray();
            } else {
                $arr[$this->cursor->getKey()] = $this->cursor->getData();
            }
        }

        return $arr;
    }

    public static function fromArray(array $arr): ArrayList
    {
        $list = new ArrayList();
        foreach ($arr as $k => $d) {
            if (is_array($d) || is_object($d)) {
                $list->add($k, self::fromArray($d));
            } else {
                $list->add($k, $d);
            }
        }

        return $list;
    }

}

?>