#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright (c) 2017 CANDY LINE INC.
#
# Licensed 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.

import argparse
import json
import socket
import struct
import os
import sys
import pkg_resources
import errno

WARN = "\033[93m"    # yellow
NOTICE = "\033[94m"  # green
ERROR = "\033[91m"   # red
END = "\033[0m"

cli_version = pkg_resources.require("candy-board-cli")[0].version

# candy apn ls ... list all apns
# candy apn set <name> <user> <password> (<id>) ... add a given apn
# candy apn del <id> ... delete a given apn
# candy network show ... show phone network state and signal strength
#                        (RSSI in dBm)
# candy sim show ... show SIM status and info (MSISDN, IMSI)
# candy modem show ... show modem info (Model, Manufacturer, Revision, IMEI)
# candy service version ... return the board service software version
# candy version ... return this CLI version

if "SOCK_PATH" in os.environ:
    SOCK_PATH = os.environ["SOCK_PATH"]
else:
    SOCK_PATH = "/var/run/candy-board-service.sock"


def err(msg):
    print(ERROR + msg + END)


def warn(msg):
    print(WARN + msg + END)


def notice(msg):
    print(NOTICE + msg + END)


def main(args):
    if args.category == "version":
        print("CANDY Board Service CLI version: %s" % cli_version)
        if not os.path.exists(SOCK_PATH):
            notice("[NOTICE] CANDY Board Service isn't running")
        return 0

    if not os.path.exists(SOCK_PATH):
        err("[ERROR] CANDY Board Service isn't running")
        return 1

    if args.category == "modem" and args.action == "reset":
        if args.yes is not True:
            warn("[WARN] Abort! Do NOT run this command "
                 "unless you understand what you're doing")
            return 1

    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    try:
        sock.connect(SOCK_PATH)
    except socket.error, v:
        errorcode = v[0]
        if errorcode == errno.ECONNREFUSED:
            err("[ERROR] CANDY Board Service isn't running")
        elif errorcode == errno.EACCES:
            err("[ERROR] Permission denied. 'sudo' is required")
        else:
            err("[ERROR] I/O error, errno=%s" % errno.errorcode[errorcode])
        return errorcode

    code = 0

    if args.category in ("apn", "network", "sim", "modem", "service"):
        try:
            # request
            packer = struct.Struct("I")
            cmd_json = json.dumps(vars(args))
            size = len(cmd_json)
            packed_header = packer.pack(size)
            sock.sendall(packed_header)
            packer = struct.Struct("%is" % size)
            packed_json = packer.pack(cmd_json)
            sock.sendall(packed_json)

            # response
            packer = struct.Struct("I")
            packed_result = sock.recv(packer.size)
            result = packer.unpack(packed_result)

            # result
            if result != 0:
                packer = struct.Struct("%is" % result)
                packed_message = sock.recv(packer.size)
                message_json = packer.unpack(packed_message)
                message = json.loads(message_json[0])
                if message["status"] != "OK":
                    code = 2
                if message["result"]:
                    print(json.dumps(message["result"], indent=2))

        finally:
            sock.close()
            return code
    else:
        raise ValueError("Unsupported category")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CANDY Board Service CLI")
    categories = parser.add_subparsers(title="categories", dest="category")

    version_commands = categories.add_parser("version", help="CLI Version")

    parser_apn = categories.add_parser("apn", help="Manipulate APN settings")
    apn_commands = parser_apn.add_subparsers(
        title="APN actions", dest="action")

    parser_apn_ls = apn_commands.add_parser("ls", help="List all APNs")
    parser_apn_set = apn_commands.add_parser("set", help="Set a new APN")
    parser_apn_set.add_argument(
        "-n", "--name", type=str, required=True, help="APN")
    parser_apn_set.add_argument(
        "-u", "--user-id", type=str, required=True, help="User ID")
    parser_apn_set.add_argument(
        "-p", "--password", type=str, required=True, help="User Password")
    parser_apn_set.add_argument(
        "-o", "--operator", type=str, required=False,
        help="Mobile Network Operator (docomo by default)")
    parser_apn_set.add_argument(
        "-i", "--id", type=str, required=False, help="APN ID (1 by default)")
    parser_apn_del = apn_commands.add_parser(
        "del", help="Delete an exsiting APN")
    parser_apn_del.add_argument(
        "-i", "--id", type=str, required=False, help="APN ID (1 by default)")

    parser_network = categories.add_parser(
        "network", help="Manage Phone Network")
    network_commands = parser_network.add_subparsers(
        title="Phone Network actions", dest="action")
    parser_network_show = network_commands.add_parser(
        "show", help="Show Phone network state and Signal strength")
    parser_network_show.add_argument(
        "-o", "--opts", type=str, required=False, help="Show Options")

    parser_sim = categories.add_parser("sim", help="Manage SIM")
    sim_commands = parser_sim.add_subparsers(
        title="SIM actions", dest="action")
    parser_sim_show = sim_commands.add_parser(
        "show", help="Show SIM state and SIM information")

    parser_modem = categories.add_parser(
        "modem", help="Manage LTE/3G module modem")
    modem_commands = parser_modem.add_subparsers(
        title="Modem actions", dest="action")
    parser_modem_show = modem_commands.add_parser(
        "show", help="Show Module information")
    parser_modem_reset = modem_commands.add_parser(
        "reset", help="Reset modem")
    parser_modem_reset.add_argument(
        "-y", "--yes", action="store_const", const=True, required=False,
        help="Use this flag if you REALLY understand what "
        "the 'modem reset' command causes")
    parser_modem_reset.add_argument(
        "-o", "--opts", type=str, required=False, help="Reset Options")

    parser_service = categories.add_parser(
        "service", help="Manage CANDY Board Service")
    service_commands = parser_service.add_subparsers(
        title="CANDY Board Service actions", dest="action")
    parser_service_version = service_commands.add_parser(
        "version", help="Show CANDY Board Service software version")

    args = parser.parse_args()
    sys.exit(main(args))
