"""
Flask web UI for HPGL plotter.
Run from project root so Command, Program, Plotter, hpgl_svg, config can be imported.
"""
import os
import sys
import uuid
from flask import Flask, request, jsonify, session, send_from_directory, render_template

# Run from project root (parent of webui)
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if ROOT not in sys.path:
    sys.path.insert(0, ROOT)

from Command import Command
from Program import Program
from Plotter import Plotter
from hpgl_svg import program_to_svg

try:
    from config import webui_secret_key, webui_max_upload_mb
except ImportError:
    webui_secret_key = lambda: os.environ.get('SECRET_KEY', 'change-me-in-production')
    webui_max_upload_mb = lambda: 16

app = Flask(__name__, static_folder='static', template_folder='templates')
app.secret_key = webui_secret_key()
app.config['MAX_CONTENT_LENGTH'] = webui_max_upload_mb() * 1024 * 1024
UPLOAD_DIR = os.path.join(ROOT, 'webui', 'uploads')
os.makedirs(UPLOAD_DIR, exist_ok=True)


def _get_program():
    path = session.get('upload_path')
    if not path or not os.path.isfile(path):
        return None
    try:
        return Program.parsefile(path)
    except Exception:
        return None


def _save_program(program, path=None):
    path = path or session.get('upload_path')
    if not path:
        return None
    with open(path, 'w') as f:
        f.write(str(program))
    return path


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/api/upload', methods=['POST'])
def upload():
    if 'file' not in request.files:
        return jsonify({'error': 'No file'}), 400
    f = request.files['file']
    if not f.filename or not (f.filename.lower().endswith('.hpgl') or f.filename.lower().endswith('.plt')):
        return jsonify({'error': 'Need an .hpgl or .plt file'}), 400
    try:
        content = f.read().decode('utf-8', errors='replace')
        Program.parse(content)  # validate
    except Exception as e:
        return jsonify({'error': 'Invalid HPGL: {}'.format(str(e))}), 400
    name = str(uuid.uuid4()) + '.hpgl'
    path = os.path.join(UPLOAD_DIR, name)
    with open(path, 'w') as out:
        out.write(content)
    session['upload_path'] = path
    session['upload_filename'] = f.filename
    return jsonify({
        'ok': True,
        'filename': f.filename,
        'preview_url': '/api/svg'
    })


@app.route('/api/dimensions')
def dimensions():
    """Return current program size in plotter units (e.g. 0.025 mm per unit)."""
    p = _get_program()
    if p is None:
        return jsonify({'error': 'No file loaded'}), 404
    w, h = p.winsize
    return jsonify({
        'width': round(w, 1),
        'height': round(h, 1),
        'width_mm': round(w * 0.025, 2),
        'height_mm': round(h * 0.025, 2),
    })


@app.route('/api/svg')
def get_svg():
    p = _get_program()
    if p is None:
        return jsonify({'error': 'No file loaded'}), 404
    width = request.args.get('width', 800, type=int)
    height = request.args.get('height', 600, type=int)
    show_scale = request.args.get('scale', '1').lower() in ('1', 'true', 'yes')
    svg = program_to_svg(p, width=width, height=height, show_scale_bar=show_scale)
    return svg, 200, {'Content-Type': 'image/svg+xml; charset=utf-8'}


@app.route('/api/transform', methods=['POST'])
def transform():
    p = _get_program()
    if p is None:
        return jsonify({'error': 'No file loaded'}), 404
    data = request.get_json() or {}
    action = data.get('action')
    if action == 'flip':
        p = p.flip()
    elif action == 'rotate':
        angle = data.get('angle', 90)
        p = p.rotate(angle)
    elif action == 'scale':
        factor = data.get('factor', 1.5)
        p = p * factor
    elif action == 'centralize':
        p = p - p.center
    elif action == 'scale_to_bounds':
        xmin = data.get('xmin')
        ymin = data.get('ymin')
        xmax = data.get('xmax')
        ymax = data.get('ymax')
        if xmin is not None and ymin is not None and xmax is not None and ymax is not None:
            if xmax <= xmin or ymax <= ymin:
                return jsonify({'error': 'Invalid bounds: xmax > xmin and ymax > ymin required.'}), 400
            p = p.fitin((xmin, ymin, xmax, ymax), (0.5, 0.5))
        else:
            try:
                plt = Plotter()
                if getattr(plt, 'boundaries', None) is None:
                    return jsonify({'error': 'Plotter not connected. Connect plotter or send bounds (xmin, ymin, xmax, ymax).'}), 400
                p = plt.full(p)
            except (OSError, AttributeError, TypeError):
                return jsonify({'error': 'Plotter not connected. Connect plotter or send bounds (xmin, ymin, xmax, ymax).'}), 400
    else:
        return jsonify({'error': 'Unknown action'}), 400
    _save_program(p)
    return jsonify({'ok': True, 'preview_url': '/api/svg'})


@app.route('/api/print', methods=['POST'])
def print_to_plotter():
    p = _get_program()
    if p is None:
        return jsonify({'error': 'No file loaded'}), 404
    try:
        plt = Plotter()
        if getattr(plt, 'ser', None) is None:
            return jsonify({'error': 'Plotter not connected. Connect plotter and try again.'}), 503
        p = plt.full(p)
        p = plt.centralize(p)
        plt.write(p)
        return jsonify({'ok': True})
    except OSError as e:
        return jsonify({'error': 'Plotter not connected: {}'.format(str(e))}), 503
    except Exception as e:
        return jsonify({'error': str(e)}), 500


@app.route('/api/status')
def status():
    has_file = bool(session.get('upload_path')) and os.path.isfile(session.get('upload_path'))
    plotter_ready = False
    try:
        plt = Plotter()
        if getattr(plt, 'ser', None) is not None and getattr(plt, 'boundaries', None) is not None:
            plotter_ready = plt.ready
    except (OSError, AttributeError, TypeError):
        pass
    return jsonify({
        'has_file': has_file,
        'filename': session.get('upload_filename'),
        'plotter_ready': plotter_ready
    })


@app.route('/api/plotter_info')
def plotter_info():
    """Return plotter connection and media dimensions for display under the print button."""
    try:
        from config import plotter_port, plotter_baudrate
    except ImportError:
        plotter_port = lambda: '/dev/ttyUSB0'
        plotter_baudrate = lambda: 9600
    port = plotter_port()
    baudrate = plotter_baudrate()
    connected = False
    ready = False
    boundaries = None
    winsize_units = None
    winsize_mm = None
    try:
        plt = Plotter()
        if getattr(plt, 'ser', None) is not None and getattr(plt, 'boundaries', None) is not None:
            connected = True
            ready = plt.ready
            try:
                boundaries = {
                    'xmin': plt.xmin,
                    'ymin': plt.ymin,
                    'xmax': plt.xmax,
                    'ymax': plt.ymax,
                }
                w, h = plt.winsize
                winsize_units = {'width': round(w, 1), 'height': round(h, 1)}
                winsize_mm = {'width': round(w * 0.025, 1), 'height': round(h * 0.025, 1)}
            except (AttributeError, TypeError):
                pass
    except (OSError, AttributeError, TypeError):
        pass
    return jsonify({
        'port': port,
        'baudrate': baudrate,
        'connected': connected,
        'ready': ready,
        'boundaries': boundaries,
        'winsize_units': winsize_units,
        'winsize_mm': winsize_mm,
    })


if __name__ == '__main__':
    try:
        from config import webui_host, webui_port
    except ImportError:
        webui_host = lambda: '0.0.0.0'
        webui_port = lambda: 5000
    app.run(
        host=webui_host(),
        port=webui_port(),
        debug=os.environ.get('FLASK_DEBUG', '0') == '1'
    )
