#!/usr/bin/env python3
import include_homekit
import hikvision, xmeye

from enum import Enum, auto
from typing import Optional
from argparse import ArgumentParser, ArgumentError
from homekit.util import validate_ipv4_or_hostname
from homekit.camera import IpcamConfig, CameraType

ipcam_config = IpcamConfig()


class Action(Enum):
    GET_NTP = auto()
    SET_NTP = auto()


def process_camera(host: str,
                   action: Action,
                   login: str,
                   password: str,
                   camera_type: CameraType,
                   ntp_server: Optional[str] = None):
    if camera_type.is_hikvision():
        client = hikvision.ISAPIClient(host)
        try:
            client.auth(login, password)
            if action == Action.GET_NTP:
                print(f'[{host}] {client.get_ntp_server()}')
                return

            client.set_ntp_server(ntp_server)
            print(f'[{host}] done')

        except hikvision.AuthError as e:
            print(f'[{host}] ({str(e)})')

        except hikvision.ResponseError as e:
            print(f'[{host}] ({str(e)})')

    elif camera_type.is_xmeye():
        try:
            client = xmeye.XMEyeCamera(hostname=host, username=login, password=password)
            client.login()

            if action == Action.GET_NTP:
                print(f'[{host}] {client.get_ntp_server()}')
                return

            client.set_ntp_server(ntp_server)
            print(f'[{host}] done')

        except OSError as e:
            print(f'[{host}] ({str(e)})')


def main():
    camera_types = ['hikvision', 'xmeye']
    parser = ArgumentParser()
    parser.add_argument('--camera', type=str)
    parser.add_argument('--camera-type', type=str, choices=camera_types)
    parser.add_argument('--all', action='store_true')
    parser.add_argument('--all-of-type', type=str, choices=camera_types)
    parser.add_argument('--get-ntp-server', action='store_true')
    parser.add_argument('--set-ntp-server', type=str)
    parser.add_argument('--username', type=str)
    parser.add_argument('--password', type=str)
    args = parser.parse_args()

    if args.all and args.all_of_type:
        raise ArgumentError(None, 'you can\'t pass both --all and --all-of-type')

    if not args.camera and not args.all and not args.all_of_type:
        raise ArgumentError(None, 'either --all, --all-of-type <TYPE> or --camera <NUM> is required')

    if not args.get_ntp_server and not args.set_ntp_server:
        raise ArgumentError(None, 'either --get-ntp-server or --set-ntp-server is required')

    action = Action.GET_NTP if args.get_ntp_server else Action.SET_NTP
    login = args.username if args.username else ipcam_config['web_creds']['login']
    password = args.password if args.password else ipcam_config['web_creds']['password']

    if action == Action.SET_NTP:
        if not args.set_ntp_server:
            raise ArgumentError(None, '--set-ntp-server is required')
        if not validate_ipv4_or_hostname(args.set_ntp_server):
            raise ArgumentError(None, 'input ntp server is neither ip address nor a valid hostname')

    kwargs = {}
    if args.set_ntp_server:
        kwargs['ntp_server'] = args.set_ntp_server
    if not args.all and not args.all_of_type:
        if not args.camera_type:
            raise ArgumentError(None, '--camera-type is required')

        if not ipcam_config.has_camera(int(args.camera)):
            raise ArgumentError(None, f'invalid camera {args.camera}')
        camera_host = ipcam_config.get_camera_ip(args.camera)

        if args.camera_type == 'hikvision':
            camera_type = CameraType.HIKVISION_264
        elif args.camera_type == 'xmeye':
            camera_type = CameraType.XMEYE
        else:
            raise ValueError('invalid --camera-type')
        process_camera(camera_host, action, login, password, camera_type, **kwargs)
    else:
        for cam in ipcam_config.get_all_cam_names():
            if not ipcam_config.is_camera_enabled(cam):
                continue

            cam_type = ipcam_config.get_camera_type(cam)
            if args.all_of_type == 'hikvision' and not cam_type.is_hikvision():
                continue
            if args.all_of_type == 'xmeye' and not ipcam_config.get_camera_type(cam).is_xmeye():
                continue
            process_camera(ipcam_config.get_camera_ip(cam), action, login, password, cam_type, **kwargs)


if __name__ == '__main__':
    main()
