/**
 * Cast Conductor Proprietary License v5
 * SPDX-License-Identifier: LicenseRef-CastConductor-Proprietary-v5
 * 
 * Copyright (c) 2025 CastConductor.com. All Rights Reserved.
 * 
 * This file is part of Cast Conductor ("Software"). The Software and its source
 * code constitute proprietary, confidential, and trade secret information of
 * CastConductor.com ("Company"). Any access or use is governed strictly by the
 * Cast Conductor Proprietary License v5 ("License"). By installing, copying,
 * accessing, compiling, or otherwise using the Software you agree to be bound by
 * all terms of the License. If you do not agree, you must cease use immediately.
 * 
 * Key Terms (Summary – see full License for binding terms):
 *  1. No Redistribution: You may not publish, distribute, sublicense, rent,
 *     lease, transfer, sell, or otherwise make the Software (or any derivative)
 *     available to any third party without prior written consent of Company.
 *  2. No Modification: Modification, reverse engineering, decompilation, or
 *     disassembly is prohibited except to the limited extent expressly permitted
 *     by applicable law that cannot be contractually waived.
 *  3. Confidentiality: Treat all source code and related artifacts as Company
 *     Confidential Information. Maintain at least the same degree of care as for
 *     your own confidential materials, and not less than reasonable care.
 *  4. No Patent License: No express or implied patent rights are granted. Future
 *     patents (if any) are fully reserved.
 *  5. No Trademark License: Company names, marks, and logos may not be used
 *     without prior written permission.
 *  6. Limited Internal Use: Use is limited solely to internal evaluation and
 *     operation of licensed Cast Conductor deployments. Commercial hosting or
 *     resale as a service requires a separate written agreement.
 *  7. Telemetry & License Validation: The Software may periodically transmit a
 *     hashed installation identifier, domain (or site ID), plugin/app version,
 *     and a truncated (non-reversible) fragment of the license key solely to
 *     validate activation status and enforce licensing. This minimal "phone home"
 *     check contains no personal or content data. If optional telemetry is later
 *     introduced it will be limited to aggregate operational metrics (no PII),
 *     fully documented, and optionally disableable per published instructions.
 *  8. Third-Party Components: The Software may include open source components
 *     covered by their own licenses. See THIRD-PARTY-NOTICES.md. Those licenses
 *     govern their respective components; this License governs all remaining code.
 *  9. Export Compliance: You are responsible for compliance with all applicable
 *     export control and sanctions laws.
 * 10. Warranty Disclaimer: THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 *     ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY,
 *     FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
 * 11. Limitation of Liability: IN NO EVENT WILL COMPANY OR AUTHORS BE LIABLE FOR
 *     ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE
 *     DAMAGES, OR LOST PROFITS, EVEN IF ADVISED OF THE POSSIBILITY.
 * 12. Acceptance: Use of the Software constitutes acceptance of the License.
 * 13. Enforcement: Unauthorized reproduction or distribution may result in civil
 *     and criminal penalties and will be prosecuted to the maximum extent allowed
 *     by law.
 * 
 * Authoritative EULA: EULA-v5.1.md (repository root – private) and https://castconductor.com/eula
 * Precedence: If this summary conflicts with the EULA, the EULA governs.
 * Revision: Current EULA revision v5.1 (subject to update; check EULA for current enterprise thresholds).
 * 
 * Full License text available from: licensing@castconductor.com
 * Security reports: security@castconductor.com
 * Commercial inquiries: licensing@castconductor.com
 * 
 * END OF HEADER
 * 
 * Geometry conversion & preset application (extracted Phase 2.7 Step 2)
 */

const LOGICAL_W = 1280;
const LOGICAL_H = 720;
const MIN_SIZE = 40;

export function fixedToPercent(rect, canvasBounds={ w:LOGICAL_W, h:LOGICAL_H }) {
	if (!rect) return null;
	const clamp = (v,min,max)=>Math.max(min,Math.min(max,v));
	let { x=0,y=0,w=0,h=0 } = rect;
	w = clamp(w, MIN_SIZE, canvasBounds.w);
	h = clamp(h, MIN_SIZE, canvasBounds.h);
	x = clamp(x, 0, canvasBounds.w - w);
	y = clamp(y, 0, canvasBounds.h - h);
	const p = (v,total)=>(v/total)*100;
	const pct = {
		x_pct:+(p(x,LOGICAL_W).toFixed(4)),
		y_pct:+(p(y,LOGICAL_H).toFixed(4)),
		w_pct:+(p(w,LOGICAL_W).toFixed(4)),
		h_pct:+(p(h,LOGICAL_H).toFixed(4)),
		anchor:'tl'
	};
	if (x <= 0.5) pct.x_pct = 0; // edge pin
	if (y <= 0.5) pct.y_pct = 0;
	return pct;
}

export function percentToFixed(pct) {
	if (!pct) return null;
	const v = (val,total)=>Math.round((val/100)*total);
	return {
		x: v(pct.x_pct||0, LOGICAL_W),
		y: v(pct.y_pct||0, LOGICAL_H),
		w: Math.max(MIN_SIZE, v(pct.w_pct||0, LOGICAL_W)),
		h: Math.max(MIN_SIZE, v(pct.h_pct||0, LOGICAL_H))
	};
}

// Canonical preset geometry map - uses 1280×720 authoring space coordinates
// (Roku scales these up to 1920×1080 at playback time)
export const PRESET_GEOMETRY = Object.freeze({
	lower_third: { w:1280,h:240,x:0,y:480 },
	upper_third: { w:1280,h:240,x:0,y:0 },
	center_third: { w:1280,h:240,x:0,y:240 },
	full_screen: { w:1280,h:720,x:0,y:0 },
	left_half: { w:640,h:720,x:0,y:0 },
	right_half: { w:640,h:720,x:640,y:0 },
	top_half: { w:1280,h:360,x:0,y:0 },
	bottom_half: { w:1280,h:360,x:0,y:360 },
	upper_left_quarter: { w:640,h:360,x:0,y:0 },
	upper_right_quarter: { w:640,h:360,x:640,y:0 },
	lower_left_quarter: { w:640,h:360,x:0,y:360 },
	lower_right_quarter: { w:640,h:360,x:640,y:360 },
	horizontal_upper_half: { w:1280,h:360,x:0,y:0 },
	horizontal_lower_half: { w:1280,h:360,x:0,y:360 }
});

/**
 * Normalize preset key to canonical map key (underscores)
 */
export function normalizePresetKey(key) {
	return String(key).toLowerCase().trim()
		.replace(/\s+/g,'_')
		.replace(/-/g,'_');
}

/**
 * Apply a preset geometry (pure). Returns new layout object (mutates provided layout for convenience) and boolean changed.
 */
export function applyPresetGeometry(layout, key, presetMap = PRESET_GEOMETRY) {
	if (!layout) return { changed:false, layout:null };
	const norm = normalizePresetKey(key);
	const before = { w:layout.width, h:layout.height, x:layout.position?.x, y:layout.position?.y };
	const apply = (w,h,x,y) => {
		if (!layout.position) layout.position = { x:0,y:0 };
		if (layout.width === w && layout.height === h && layout.position.x === x && layout.position.y === y) return false;
		layout.width = w; layout.height = h; layout.position.x = x; layout.position.y = y; layout.x_position = x; layout.y_position = y; return true;
	};
	let changed = false;
	const variants = new Set([
		norm,
		norm.replace(/quarter/,'quarter'),
		norm.replace(/third/,'third'),
		norm.replace(/half/,'half')
	]);
	for (const k of variants) {
		if (presetMap[k]) { changed = apply(presetMap[k].w, presetMap[k].h, presetMap[k].x, presetMap[k].y) || changed; break; }
	}
	// Fallback legacy switch if not found (handles older UI option values)
	// Uses 1280×720 authoring space coordinates
	if (!changed) {
		switch (norm) {
			case 'lower_third': changed = apply(1280,240,0,480); break;
			case 'upper_third': changed = apply(1280,240,0,0); break;
			case 'center_third': changed = apply(1280,240,0,240); break;
			case 'full_screen': changed = apply(1280,720,0,0); break;
			case 'left_half': changed = apply(640,720,0,0); break;
			case 'right_half': changed = apply(640,720,640,0); break;
			case 'top_half': changed = apply(1280,360,0,0); break;
			case 'bottom_half': changed = apply(1280,360,0,360); break;
			case 'upper_left_quarter': changed = apply(640,360,0,0); break;
			case 'upper_right_quarter': changed = apply(640,360,640,0); break;
			case 'lower_left_quarter': changed = apply(640,360,0,360); break;
			case 'lower_right_quarter': changed = apply(640,360,640,360); break;
		}
	}
	// Self-heal drift if preset exists
	const exp = presetMap[norm];
	if (exp) {
		const drift = layout.width !== exp.w || layout.height !== exp.h || layout.position.x !== exp.x || layout.position.y !== exp.y;
		if (drift) {
			layout.width = exp.w; layout.height = exp.h; layout.position.x = exp.x; layout.position.y = exp.y; layout.x_position = exp.x; layout.y_position = exp.y;
		}
	}
	return { changed, layout, before };
}
