package net.northking.cctp.upperComputer.driver.ios.command.data;


import net.northking.cctp.upperComputer.driver.ios.packet.CodecUtils;
import net.northking.cctp.upperComputer.driver.ios.packet.ICommandData;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


public class UiNodeData implements ICommandData {
    private int x;
    private int y;
    private int width;
    private int height;
    private String label = null;
    private String name = null;
    private String type = "Unknown";
    private String value = null;
    private UiNodeData parent = null;
    private ArrayList<UiNodeData> children = new ArrayList<>();

    public UiNodeData() {

    }

    public UiNodeData(UiNodeData parent) {
        this.parent = parent;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public ArrayList<UiNodeData> getChildren() {
        return children;
    }

    public void setChildren(ArrayList<UiNodeData> children) {
        this.children = children;
    }

    public UiNodeData getParent() {
        return parent;
    }

    public void findByLabel(String label, ArrayList<UiNodeData> resultList) {
        if (Objects.equals(this.label, label)) {
            resultList.add(this);
        }
        for (UiNodeData node : children) {
            node.findByLabel(label, resultList);
        }
    }

    public void findByName(String name, ArrayList<UiNodeData> resultList) {
        if (Objects.equals(this.name, name)) {
            resultList.add(this);
        }
        for (UiNodeData node : children) {
            node.findByName(name, resultList);
        }
    }

    public void findByValue(String value, ArrayList<UiNodeData> resultList) {
        if (Objects.equals(this.value, value)) {
            resultList.add(this);
        }
        for (UiNodeData node : children) {
            node.findByValue(value, resultList);
        }
    }

    public boolean isTextFieldHasValue() {
        return label != null || name != null || value != null;
    }

    @Override
    public void buildFromInput(DataInput dataInput) throws IOException {
        x = dataInput.readInt();
        y = dataInput.readInt();
        width = dataInput.readInt();
        height = dataInput.readInt();
        label = CodecUtils.readNullable(dataInput, CodecUtils::readText);
        name = CodecUtils.readNullable(dataInput, CodecUtils::readText);
        type = CodecUtils.readText(dataInput);
        value = CodecUtils.readNullable(dataInput, CodecUtils::readText);
        int childrenCount = dataInput.readInt();
        children = new ArrayList<>(childrenCount);
        for (int i = 0; i < childrenCount; i++) {
            UiNodeData child = new UiNodeData(this);
            child.buildFromInput(dataInput);
            children.add(child);
        }
    }

    @Override
    public void encodeToOutput(DataOutput dataOutput) throws IOException {
        dataOutput.writeInt(x);
        dataOutput.writeInt(y);
        dataOutput.writeInt(width);
        dataOutput.writeInt(height);
        CodecUtils.writeNullable(dataOutput, label, CodecUtils::writeText);
        CodecUtils.writeNullable(dataOutput, name, CodecUtils::writeText);
        CodecUtils.writeText(dataOutput, type);
        CodecUtils.writeNullable(dataOutput, value, CodecUtils::writeText);
        dataOutput.writeInt(children.size());
        for (UiNodeData child : children) {
            child.encodeToOutput(dataOutput);
        }
    }

    public void childrenToArrayList(ArrayList<UiNodeData> list) {
        for (UiNodeData child : children) {
            list.add(child);
            child.childrenToArrayList(list);
        }
    }

    public SearchUiNodeData toSearchNode() {
        SearchUiNodeData searchUiNodeData = new SearchUiNodeData();
        searchUiNodeData.setType(type);
        searchUiNodeData.setLabel(label);
        searchUiNodeData.setName(name);
        searchUiNodeData.setValue(value);
        return searchUiNodeData;
    }

    public int inParentIndex() {
        if (parent != null) {
            return parent.children.indexOf(this);
        }
        return -1;
    }

    public SearchUiNodeData.SearchChain toSearchChain() {
        int inParentChildIndex = inParentIndex();
        SearchUiNodeData.SearchChain currentChain = new SearchUiNodeData.SearchChain(this, inParentChildIndex, null);
        UiNodeData rootNode = this;
        while (rootNode.parent != null) {
            rootNode = rootNode.parent;
        }
        ArrayList<UiNodeData> allNodes = new ArrayList<UiNodeData>();
        allNodes.add(rootNode);
        rootNode.childrenToArrayList(allNodes);

        List<UiNodeData> matchedList = new ArrayList<UiNodeData>();
        do {
            matchedList.clear();
            for (UiNodeData node : allNodes) {
                if (node.matchChain(currentChain)) {
                    matchedList.add(node);
                }
            }

            if (!currentChain.createNext()) {
                //条件到顶了仍有两个？不太可能
                break;
            }
        } while (matchedList.size() > 2);
        currentChain.calculateSearchIndex();
        return currentChain;

    }

    private boolean matchChain(SearchUiNodeData.SearchChain chain) {
        if (inParentIndex() == chain.getInParentIndex() && equalSearchField(chain.getNode())) {
            if (chain.isFloor()) {
                return true;
            } else {
                return parent.matchChain(chain.getParent());
            }
        }
        return false;
    }

    public boolean equalSearchField(UiNodeData nodeData) {
        return type.equals(nodeData.type)
                && Objects.equals(label, nodeData.label)
                && Objects.equals(name, nodeData.name)
                && Objects.equals(value, nodeData.value);
    }

    @Override
    public String toString() {
        return "UiNodeData{" +
                "x=" + x +
                ", y=" + y +
                ", width=" + width +
                ", height=" + height +
                ", label='" + label + '\'' +
                ", name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", value='" + value + '\'' +
                ", children=" + children +
                '}';
    }
}

