/*
 Licensed to the Apache Software Foundation (ASF) under one
 or more contributor license agreements.  See the NOTICE file
 distributed with this work for additional information
 regarding copyright ownership.  The ASF licenses this file
 to you under the Apache License, Version 2.0 (the
 "License"); you may not use this file except in compliance
 with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on an
 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 */
package org.apache.plc4x.java.ads.api.commands.types;

import static java.lang.Long.toHexString;
import static java.util.Objects.requireNonNull;

/**
 * Based on spec from: https://infosys.beckhoff.com/content/1033/tcadscommon/html/ads_returncodes.htm
 */
// Error codes: 0x000..., 0x500..., 0x700..., 0x1000..., 0x274C...
public enum AdsReturnCode {
    // Global Error Codes
    ADS_CODE_0(0x0, 0, "no error"),
    ADS_CODE_1(0x1, 1, "Internal error"),
    ADS_CODE_2(0x2, 2, "No Rtime"),
    ADS_CODE_3(0x3, 3, "Allocation locked memory error"),
    ADS_CODE_4(0x4, 4, "Insert mailbox error", "No ADS mailbox was available to process this message.", "Reduce the number of ADS calls (e.g ADS-Sum commands or Max Delay Parameter)"),
    ADS_CODE_5(0x5, 5, "Wrong receive HMSG"),
    ADS_CODE_6(0x6, 6, "target port not found", "ADS Server not started"),
    ADS_CODE_7(0x7, 7, "target machine not found", "Missing ADS routes"),
    ADS_CODE_8(0x8, 8, "Unknown command ID"),
    ADS_CODE_9(0x9, 9, "Bad task ID"),
    ADS_CODE_10(0xA, 10, "No IO"),
    ADS_CODE_11(0xB, 11, "Unknown ADS command"),
    ADS_CODE_12(0xC, 12, "Win 32 error"),
    ADS_CODE_13(0xD, 13, "Port not connected"),
    ADS_CODE_14(0xE, 14, "Invalid ADS length"),
    ADS_CODE_15(0xF, 15, "Invalid ADS Net ID"),
    ADS_CODE_16(0x10, 16, "Low Installation level"),
    ADS_CODE_17(0x11, 17, "No debug available"),
    ADS_CODE_18(0x12, 18, "Port disabled"),
    ADS_CODE_19(0x13, 19, "Port already connected"),
    ADS_CODE_20(0x14, 20, "ADS Sync Win32 error"),
    ADS_CODE_21(0x15, 21, "ADS Sync Timeout"),
    ADS_CODE_22(0x16, 22, "ADS Sync AMS error"),
    ADS_CODE_23(0x17, 23, "ADS Sync no index map"),
    ADS_CODE_24(0x18, 24, "Invalid ADS port"),
    ADS_CODE_25(0x19, 25, "No memory"),
    ADS_CODE_26(0x1A, 26, "TCP send error"),
    ADS_CODE_27(0x1B, 27, "Host unreachable"),
    ADS_CODE_28(0x1C, 28, "Invalid AMS fragment"),


    // Router Error Codes
    ADS_CODE_1280(0x500, 1280, "ROUTERERR_NOLOCKEDMEMORY", "No locked memory can be allocated"),
    ADS_CODE_1281(0x501, 1281, "ROUTERERR_RESIZEMEMORY", "The size of the  router memory could not be changed"),
    ADS_CODE_1282(0x502, 1282, "ROUTERERR_MAILBOXFULL", "The mailbox has reached the maximum number of possible messages. The current sent message was rejected", "Check the connection between the communication partners"),
    ADS_CODE_1283(0x503, 1283, "ROUTERERR_DEBUGBOXFULL ", "The mailbox has reached the maximum number of possible messages.The sent message will not be displayed in the debug monitor", "Check the connection to the debug monitor"),
    ADS_CODE_1284(0x504, 1284, "ROUTERERR_UNKNOWNPORTTYPE ", "The port type is unknown"),
    ADS_CODE_1285(0x505, 1285, "ROUTERERR_NOTINITIALIZED", "Router is not initialised"),
    ADS_CODE_1286(0x506, 1286, "ROUTERERR_PORTALREADYINUSE ", "The desired port number is already assigned"),
    ADS_CODE_1287(0x507, 1287, "ROUTERERR_NOTREGISTERED ", "Port not registered"),
    ADS_CODE_1288(0x508, 1288, "ROUTERERR_NOMOREQUEUES", "The maximum number of Ports reached"),
    ADS_CODE_1289(0x509, 1289, "ROUTERERR_INVALIDPORT ", "The port is invalid."),
    ADS_CODE_1290(0x50A, 1290, "ROUTERERR_NOTACTIVATED ", "TwinCAT Router not active"),
    ADS_CODE_1291(0x50B, 1291, "ROUTERERR_FRAGMENTBOXFULL"),
    ADS_CODE_1292(0x50C, 1292, "ROUTERERR_FRAGMENTTIMEOUT"),
    ADS_CODE_1293(0x50D, 1293, "ROUTERERR_TOBEREMOVED"),


    // General ADS Error Codes
    ADS_CODE_1792(0x700, 1792, "error class <device error>"),
    ADS_CODE_1793(0x701, 1793, "Service is not supported by server"),
    ADS_CODE_1794(0x702, 1794, "invalid index group"),
    ADS_CODE_1795(0x703, 1795, "invalid index offset"),
    ADS_CODE_1796(0x704, 1796, "reading/writing not permitted"),
    ADS_CODE_1797(0x705, 1797, "parameter size not correct"),
    ADS_CODE_1798(0x706, 1798, "invalid parameter value(s)"),
    ADS_CODE_1799(0x707, 1799, "device is not in a ready state"),
    ADS_CODE_1800(0x708, 1800, "device is busy"),
    ADS_CODE_1801(0x709, 1801, "invalid context (must be in Windows)"),
    ADS_CODE_1802(0x70A, 1802, "out of memory"),
    ADS_CODE_1803(0x70B, 1803, "invalid parameter value(s)"),
    ADS_CODE_1804(0x70C, 1804, "not found (files, ...)"),
    ADS_CODE_1805(0x70D, 1805, "syntax error in command or file"),
    ADS_CODE_1806(0x70E, 1806, "objects do not match"),
    ADS_CODE_1807(0x70F, 1807, "object already exists"),
    ADS_CODE_1808(0x710, 1808, "symbol not found"),
    ADS_CODE_1809(0x711, 1809, "symbol version invalid", "Onlinechange", "Release handle and get a new one"),
    ADS_CODE_1810(0x712, 1810, "server is in invalid state"),
    ADS_CODE_1811(0x713, 1811, "AdsTransMode not supported"),
    ADS_CODE_1812(0x714, 1812, "Notification handle is invalid", "Onlinechange", "Release handle and get a new one"),
    ADS_CODE_1813(0x715, 1813, "Notification client not registered"),
    ADS_CODE_1814(0x716, 1814, "no more notification handles"),
    ADS_CODE_1815(0x717, 1815, "size for watch too big"),
    ADS_CODE_1816(0x718, 1816, "device not initialized"),
    ADS_CODE_1817(0x719, 1817, "device has a timeout"),
    ADS_CODE_1818(0x71A, 1818, "query interface failed"),
    ADS_CODE_1819(0x71B, 1819, "wrong interface required"),
    ADS_CODE_1820(0x71C, 1820, "class ID is invalid"),
    ADS_CODE_1821(0x71D, 1821, "object ID is invalid"),
    ADS_CODE_1822(0x71E, 1822, "request is pending"),
    ADS_CODE_1823(0x71F, 1823, "request is aborted"),
    ADS_CODE_1824(0x720, 1824, "signal warning"),
    ADS_CODE_1825(0x721, 1825, "invalid array index"),
    ADS_CODE_1826(0x722, 1826, "symbol not active", "Onlinechange", "Release handle and get a new one"),
    ADS_CODE_1827(0x723, 1827, "access denied"),
    ADS_CODE_1828(0x724, 1828, "missing license ", "", "Activate license for TwinCAT 3 function"),
    ADS_CODE_1836(0x72c, 1836, "exception occured during system start", "", "Check each device transistions"),
    ADS_CODE_1856(0x740, 1856, "Error class <client error>"),
    ADS_CODE_1857(0x741, 1857, "invalid parameter at service"),
    ADS_CODE_1858(0x742, 1858, "polling list is empty"),
    ADS_CODE_1859(0x743, 1859, "var connection already in use"),
    ADS_CODE_1860(0x744, 1860, "invoke ID in use"),
    ADS_CODE_1861(0x745, 1861, "timeout elapsed", "", "Check ADS routes of sender and receiver and your firewall setting"),
    ADS_CODE_1862(0x746, 1862, "error in win32 subsystem"),
    ADS_CODE_1863(0x747, 1863, "Invalid client timeout value"),
    ADS_CODE_1864(0x748, 1864, "ads-port not opened"),
    ADS_CODE_1872(0x750, 1872, "internal error in ads sync"),
    ADS_CODE_1873(0x751, 1873, "hash table overflow"),
    ADS_CODE_1874(0x752, 1874, "key not found in hash"),
    ADS_CODE_1875(0x753, 1875, "no more symbols in cache"),
    ADS_CODE_1876(0x754, 1876, "invalid response received"),
    ADS_CODE_1877(0x755, 1877, "sync port is locked"),


    // RTime Error Codes
    ADS_CODE_4096(0x1000, 4096, "RTERR_INTERNAL ", "Internal fatal error in the TwinCAT real-time system"),
    ADS_CODE_4097(0x1001, 4097, "RTERR_BADTIMERPERIODS", "Timer value not vaild"),
    ADS_CODE_4098(0x1002, 4098, "RTERR_INVALIDTASKPTR", "Task pointer has the invalid value ZERO"),
    ADS_CODE_4099(0x1003, 4099, "RTERR_INVALIDSTACKPTR", "Task stack pointer has the invalid value ZERO"),
    ADS_CODE_4100(0x1004, 4100, "RTERR_PRIOEXISTS", "The demand task priority is already assigned"),
    ADS_CODE_4101(0x1005, 4101, "RTERR_NOMORETCB", "No more free TCB (Task Control Block) available. Maximum number of TCBs is 64"),
    ADS_CODE_4102(0x1006, 4102, "RTERR_NOMORESEMAS", "No more free semaphores available. Maximum number of semaphores is 64"),
    ADS_CODE_4103(0x1007, 4103, "RTERR_NOMOREQUEUES", "No more free queue available. Maximum number of queue is 64"),
    ADS_CODE_4104(0x1008, 4104, "TwinCAT ", "reserved."),
    ADS_CODE_4105(0x1009, 4105, "TwinCAT ", "reserved."),
    ADS_CODE_4106(0x100A, 4106, "TwinCAT ", "reserved."),
    ADS_CODE_4107(0x100B, 4107, "TwinCAT ", "reserved."),
    ADS_CODE_4108(0x100C, 4108, "TwinCAT ", "reserved."),
    ADS_CODE_4109(0x100D, 4109, "RTERR_EXTIRQALREADYDEF", "An external synchronisation interrupt is already applied"),
    ADS_CODE_4110(0x100E, 4110, "RTERR_EXTIRQNOTDEF", "No external synchronsiation interrupt applied"),
    ADS_CODE_4111(0x100F, 4111, "RTERR_EXTIRQINSTALLFAILED", "The apply of the external synchronisation interrupt failed"),
    ADS_CODE_4112(0x1010, 4112, "RTERR_IRQLNOTLESSOREQUAL", "Call of a service function in the wrong context"),
    ADS_CODE_4119(0x1017, 4119, "RTERR_VMXNOTSUPPORTED", "Intel VT-x extension is not supported."),
    ADS_CODE_4120(0x1018, 4120, "RTERR_VMXDISABLED", "Intel VT-x extension is not enabled in BIOS."),
    ADS_CODE_4121(0x1019, 4121, "RTERR_VMXCONTROLSMISSING", "Missing feature in Intel VT-x extension."),
    ADS_CODE_4122(0x101A, 4122, "RTERR_VMXENABLEFAILS", "Enabling Intel VT-x fails."),


    // TCP Winsock Error Codes
    ADS_CODE_10060(0x274c, 10060, "A socket operation was attempted to an unreachable host", "Host unreachable", "Check network connection via ping"),
    ADS_CODE_10061(0x274d, 10061, "A connection attempt failed because the connected party did not properly respond after a period of time,or established connection failed because connected host has failed to respond.", "Host unreachable", "Check network connection via ping"),
    ADS_CODE_10065(0x2751, 10065, "No connection could be made because the target machine actively refused it"),
    UNKNOWN(0, 0, "", "", "");

    private final long hex;
    private final long dec;
    private final String description;
    private final String possibleCauses;
    private final String solution;

    AdsReturnCode(long hex, long dec, String description) {
        this(hex, dec, description, "");
    }

    AdsReturnCode(long hex, long dec, String description, String possibleCauses) {
        this(hex, dec, description, possibleCauses, "");
    }

    AdsReturnCode(long hex, long dec, String description, String possibleCauses, String solution) {
        this.hex = hex;
        this.dec = dec;
        if (hex != dec) {
            throw new IllegalArgumentException("hex " + hex + " is different from dec " + dec);
        }
        this.description = requireNonNull(description);
        this.possibleCauses = possibleCauses;
        this.solution = solution;
    }

    // TODO: optimize with map
    public static AdsReturnCode of(long hex) {
        for (AdsReturnCode adsReturnCode : values()) {
            if (adsReturnCode.hex == hex) {
                return adsReturnCode;
            }
        }
        return UNKNOWN;
    }

    public static AdsReturnCode of(String name) {
        return valueOf(name);
    }

    public static AdsReturnCode ofInt(String value) {
        return of(Long.parseLong(value));
    }

    public long getHex() {
        return hex;
    }

    public long getDec() {
        return dec;
    }

    public String getDescription() {
        return description;
    }

    public String getPossibleCauses() {
        return possibleCauses;
    }

    public String getSolution() {
        return solution;
    }

    @Override
    public String toString() {
        return "AdsReturnCode{" +
            "hex=" + String.format("0x%4s", toHexString(hex)).replace(' ', '0') +
            ", dec=" + dec +
            ", description='" + description + '\'' +
            ", possibleCauses='" + possibleCauses + '\'' +
            ", solution='" + solution + '\'' +
            '}';
    }
}
