#!/usr/bin/env python3
"""
SPOS - Small Payload Object Serializer
Copyright (C) 2020 Luiz Eduardo Amaral <luizamaral306@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
import argparse
import io
import json
import re
import sys

from spos import encode, decode
from spos.random import random_payload


def read_and_close_json(buf):
    d = json.load(buf)
    buf.close()
    return d


def _encode(infile, outfile, payload_spec, format):
    payload_data = read_and_close_json(infile)

    message = encode(payload_data, payload_spec, format)
    message = (
        message if format == "bytes" else bytes(message, encoding="ascii")
    )
    if hasattr(outfile, "name") and outfile.name == "<stdout>":
        sys.stdout.buffer.write(message)
    else:
        outfile.write(message)
    outfile.close()


def _decode(infile, outfile, payload_spec, format):
    if hasattr(infile, "name") and infile.name == "<stdin>":
        message = sys.stdin.buffer.read()
    else:
        message = infile.read()
        infile.close()

    if format != "bytes":
        message = message.decode("ascii")
        if format == "hex":
            message = (
                message
                if re.match("^0[xX][0-9a-fA-F]+$", message)
                else f"0x{message}"
            )
        elif format == "bin":
            message = (
                message if re.match("^0b[01]+$", message) else f"0b{message}"
            )

    payload_data, meta = decode(message, payload_spec)
    outfile.write(
        json.dumps({"meta": meta, "body": payload_data}, indent=2) + "\n"
    )
    outfile.close()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Spos is a tool for serializing objects."
    )

    parser.add_argument(
        "-d", "--decode", action="store_true", help="decodes a message."
    )
    parser.add_argument(
        "-p",
        "--payload-spec",
        type=argparse.FileType("r"),
        required=True,
        help="json file payload specifications.",
    )
    parser.add_argument(
        "-f",
        "--format",
        choices=["bin", "hex", "bytes"],
        default="bytes",
        help="Output format",
    )

    parser.add_argument(
        "-r",
        "--random",
        action="store_true",
        help="Creates a random message/payload_data",
    )
    parser.add_argument(
        "infile",
        nargs="?",
        type=argparse.FileType("rb"),
        default=sys.stdin,
        help="Input file",
    )

    parser.add_argument(
        "outfile",
        nargs="?",
        type=argparse.FileType("wb"),
        default=sys.stdout,
        help="Output file",
    )
    args = parser.parse_args()

    payload_spec = read_and_close_json(args.payload_spec)

    if args.random:
        args.infile.detach()
        message, payload_data = random_payload(payload_spec, output="bytes")
        if args.decode:
            args.infile = io.BytesIO(message)
        else:
            args.infile = io.BytesIO(
                bytes(json.dumps(payload_data), encoding="ascii")
            )

    if args.decode:
        _decode(args.infile, args.outfile, payload_spec, args.format)
    else:
        _encode(args.infile, args.outfile, payload_spec, args.format)
