package ru.upos;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.file.FileStore;
import java.nio.file.FileSystems;

import static ru.upos.Tag.*;

/**
 * Сбор данных для выполнения команды PingInfo.
 */
public class UposPingInfo {

    private static final String modelName = "UPOS jar";
    private static final byte controlDeviceType = 0x02;
    private static final String[] uposVersion = {"UPOS", "00.00.00"}; // attantion! use version format "nn.nn.nn"
    private static final String osTag = "OS";
    private static final String[] moduleVersion = {"upos-msb.jar", "0.0.0.0"};
    private static final byte tcpDevice = 0x04;

    // TODO: algorithm correct only for Windows, it must be for Unix too
    private String getVolumeSerialNumber() throws IOException {
        int vsn = 0;
        String currDir = System.getProperty("user.dir");

        for (FileStore store : FileSystems.getDefault().getFileStores()) {
            if (store.toString().contains(currDir.substring(0, 2))) {
                vsn = (int)store.getAttribute("volume:vsn");
            }
        }
        return String.format("P%011d", vsn);
    }

    private String getOsName() {
        return System.getProperty("os.name");
    }

    private byte getTcpOptions() {
        // TODO: return: 0x01 - DHCP, 0x00 - static address
        return 0x01;
    }

    private byte[] getIpAddress() throws UnknownHostException {
        return InetAddress.getLocalHost().getAddress();
    }

    private byte[] getTagBytes(Tag tag, byte[] value) throws IOException {
        TLV tlv = new TLV(tag, value.length, value);
        return tlv.getFullTLV();
    }

    private byte[] getTemplateBytes(Tag template, Tag[] tags, byte[][] values) throws Exception {
        ByteArrayOutputStream tagsBuf = new ByteArrayOutputStream();

        if (tags.length != values.length) {
            throw new Exception("Tags list length not equal values list length");
        }
        for (int i = 0; i < tags.length; ++i) {
            TLV tlv = new TLV(tags[i], values[i].length, values[i]);
            tagsBuf.write(tlv.getFullTLV());
        }
        TLV result = new TLV(template, tagsBuf.size(), tagsBuf.toByteArray());
        return result.getFullTLV();
    }

    public byte[] getPingInfoData() throws Exception {
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        TLV result;

        // device type
        buf.write(getTagBytes(TagCE, new byte[]{controlDeviceType}));
        // device model name
        buf.write(getTagBytes(TagC4, modelName.getBytes()));
        // serial number
        buf.write(getTagBytes(TagC5, getVolumeSerialNumber().getBytes()));
        // UPOS version (template E1)
        Tag[] tags = new Tag[]{TagC8, TagC9};
        byte[][] values = new byte[][]{uposVersion[0].getBytes(), uposVersion[1].getBytes()};
        buf.write(getTemplateBytes(TagE1, tags, values));
        // OS name (template E1)
        values = new byte[][]{osTag.getBytes(), getOsName().getBytes()};
        buf.write(getTemplateBytes(TagE1, tags, values));
        // module version (template E1)
        values = new byte[][]{moduleVersion[0].getBytes(), moduleVersion[1].getBytes()};
        buf.write(getTemplateBytes(TagE1, tags, values));
        // TCP device, TCP options, IP address (template E4)
        tags = new Tag[]{TagD3, TagD4, TagD5};
        values = new byte[][]{new byte[]{tcpDevice}, new byte[]{getTcpOptions()}, getIpAddress()};
        buf.write(getTemplateBytes(TagE4, tags, values));
        // all tags (template E2)
        result = new TLV(TagE2, buf.size(), buf.toByteArray());
        return result.getFullTLV();
    }
}
