[Back]
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class MapController extends Controller
{
    // app/Http/Controllers/MapController.php
    public function show(string $slug = 'groundfloor')
    {
        $floor = \DB::table('floors')->where('slug', $slug)->first();
        abort_unless($floor, 404);

        $floors = \DB::table('floors')->orderBy('sort_order')->get();

        $areas  = \DB::table('areas')
            ->where('floor_id', $floor->id)
            ->where('is_active', 1)
            ->orderBy('name')
            ->get();

        // ---- session state
        $currentCp = null; $destCp = null;
        $currentDot = null; $destDot = null;

        $currentId = session('nav.current_id');
        $destSlug  = session('nav.dest_slug');

        if ($currentId) $currentCp = \DB::table('checkpoints')->find($currentId);
        if ($destSlug)  $destCp    = \DB::table('checkpoints')->where('slug', $destSlug)->first();

        if ($currentCp) $currentDot = ['x'=>$currentCp->x, 'y'=>$currentCp->y];
        if ($destCp)    $destDot    = ['x'=>$destCp->x,    'y'=>$destCp->y];

        // ---- route computation containers
        $segmentsByFloor = [];            // floor_id => ["x1,y1 x2,y2", ...]
        $routeCpsByFloor = [];            // floor_id => [cp, cp, ...]
        $blinkSegmentPointsByFloor = [];  // floor_id => "x1,y1 x2,y2" (trimmed)
        $routeStepIndex = [];             // checkpoint_id => global step number (1..N)
        $nextCp = null;                   // next checkpoint after current along path
        $nextInstruction = null;          // human instruction text
        $ttsSteps = [];                   // array of short voice lines (we'll speak the first one)

        // ---- build graph & path if both ends are known
        if ($currentCp && $destCp) {
            [$nodes, $adj] = $this->buildGraph();
            $pathIds = $this->dijkstra($adj, $currentCp->id, $destCp->id);

            // global step numbers (1-based)
            foreach ($pathIds as $i => $id) {
                $routeStepIndex[$id] = $i + 1;
            }

            // collect segments per floor + list of route checkpoints per floor
            for ($i = 0; $i < count($pathIds) - 1; $i++) {
                $a = $nodes[$pathIds[$i]]   ?? null;
                $b = $nodes[$pathIds[$i+1]] ?? null;
                if (!$a || !$b) continue;

                // collect route cps (ensure uniqueness & order)
                $routeCpsByFloor[$a->floor_id] = $routeCpsByFloor[$a->floor_id] ?? [];
                if (empty($routeCpsByFloor[$a->floor_id]) || end($routeCpsByFloor[$a->floor_id])->id !== $a->id) {
                    $routeCpsByFloor[$a->floor_id][] = $a;
                }
                $routeCpsByFloor[$b->floor_id] = $routeCpsByFloor[$b->floor_id] ?? [];
                if (empty($routeCpsByFloor[$b->floor_id]) || end($routeCpsByFloor[$b->floor_id])->id !== $b->id) {
                    $routeCpsByFloor[$b->floor_id][] = $b;
                }

                // draw only same-floor segments for the baseline
                if ($a->floor_id === $b->floor_id) {
                    $segmentsByFloor[$a->floor_id] = ($segmentsByFloor[$a->floor_id] ?? []);
                    $segmentsByFloor[$a->floor_id][] = "{$a->x},{$a->y} {$b->x},{$b->y}";
                }
            }

            // find next checkpoint after current & highlight the first leg on same floor
            $idx = array_search($currentCp->id, $pathIds, true);
            if ($idx !== false && $idx < count($pathIds) - 1) {
                $nextCp = $nodes[$pathIds[$idx + 1]] ?? null;

                if ($nextCp) {
                    // Cross-floor? tell user to move floors
                    if ($nextCp->floor_id !== $currentCp->floor_id) {
                        $nextFloor = \DB::table('floors')->where('id', $nextCp->floor_id)->first();
                        $nextFloorName = $nextFloor->name ?? 'next floor';
                        $nextInstruction = "Use the stairs or lift to reach {$nextFloorName}, then scan the QR there.";
                        $ttsSteps[] = "Use the stairs or lift to reach {$nextFloorName}.";
                    } else {
                        // Same-floor: trim the end of the blinking segment so the arrow doesn't touch the next checkpoint
                        $nextInstruction = "Walk to {$nextCp->name} and scan the QR there.";

                        $x1 = (float)$currentCp->x;  $y1 = (float)$currentCp->y;
                        $x2 = (float)$nextCp->x;     $y2 = (float)$nextCp->y;
                        $dx = $x2 - $x1; $dy = $y2 - $y1;
                        $len = sqrt($dx*$dx + $dy*$dy);
                        $gapPx = 14.0;

                        if ($len > 0) {
                            $ux = $dx / $len; $uy = $dy / $len;   // unit vector
                            $trim = min($gapPx, max(0.0, $len - 1.0)); // avoid over-trimming
                            $tx = $x2 - $ux * $trim;
                            $ty = $y2 - $uy * $trim;
                            $blinkSegmentPointsByFloor[$currentCp->floor_id] = sprintf('%.0f,%.0f %.0f,%.0f', $x1, $y1, $tx, $ty);
                        }

                        // Build a concise voice line for the first leg.
                        // Optionally hint about the next turn if we have one more segment.
                        $line = "Walk to {$nextCp->name}.";
                        if ($idx + 2 < count($pathIds)) {
                            $b = $nodes[$pathIds[$idx + 1]];      // next
                            $c = $nodes[$pathIds[$idx + 2]];      // next after next
                            // only attempt turn if same floor
                            if ($b && $c && $b->floor_id === $c->floor_id) {
                                $ax = $currentCp->x; $ay = $currentCp->y;
                                $bx = $b->x; $by = $b->y;
                                $cx = $c->x; $cy = $c->y;
                                $turn = $this->turnHint($ax,$ay,$bx,$by,$cx,$cy);
                                if ($turn) $line .= " Then {$turn}.";
                            }
                        }
                        $ttsSteps[] = $line;
                    }
                }
            }

            // If the current is already the destination, say arrived.
            if (!empty($pathIds) && end($pathIds) === $currentCp->id) {
                $ttsSteps = ["You have arrived at {$destCp->name}."]; // overrides
                $nextInstruction = "You have arrived at {$destCp->name}.";
            }
        }

        // ---- all checkpoints on this floor (for context & dropdown)
        $floorCheckpoints = \DB::table('checkpoints')
            ->where('floor_id', $floor->id)
            ->orderBy('name')
            ->get(['id','slug','name','x','y','type']);

        // choose a reasonable TTS language; swap to 'ms-MY' if you record Malay names
        $ttsLang = 'en-US';

        return view('map.show', compact(
            'floor','floors','areas',
            'segmentsByFloor','routeCpsByFloor','blinkSegmentPointsByFloor',
            'currentDot','destDot','currentCp','destCp',
            'nextCp','nextInstruction','floorCheckpoints','routeStepIndex',
            'ttsSteps','ttsLang'
        ));
    }

    /**
     * Small helper: hint a turn direction at the middle point (b) between (a)->(b)->(c).
     * Returns 'turn left', 'turn right', or 'continue straight' (or '' if unknown).
     */
    private function turnHint($ax,$ay,$bx,$by,$cx,$cy): string
    {
        $v1x = $bx - $ax; $v1y = $by - $ay;
        $v2x = $cx - $bx; $v2y = $cy - $by;

        $len1 = sqrt($v1x*$v1x + $v1y*$v1y);
        $len2 = sqrt($v2x*$v2x + $v2y*$v2y);
        if ($len1 == 0 || $len2 == 0) return '';

        // normalize
        $v1x /= $len1; $v1y /= $len1;
        $v2x /= $len2; $v2y /= $len2;
        
        // Y-DOWN screen coords: cross>0 means RIGHT, cross<0 means LEFT
        $cross = $v1x*$v2y - $v1y*$v2x;
        $dot   = $v1x*$v2x + $v1y*$v2y;
        $angle = atan2($cross, $dot) * 180.0 / M_PI; // only for magnitude

        // tune thresholds as you like
        if (abs($angle) > 155) return 'make a U-turn';
        if (abs($angle) < 25)  return 'continue straight';

        return ($cross > 0) ? 'turn right' : 'turn left';
    }






    // ----- Helpers: graph + Dijkstra -----
    private function buildGraph(): array
    {
        $nodes = DB::table('checkpoints')->get()->keyBy('id');
        $edges = DB::table('checkpoint_links')->get();

        $adj = [];
        foreach ($edges as $e) {
            if (!isset($nodes[$e->from_id]) || !isset($nodes[$e->to_id])) continue;
            $from = $nodes[$e->from_id];
            $to   = $nodes[$e->to_id];
            $w    = $e->weight ?? $this->euclid($from->x, $from->y, $to->x, $to->y);
            $adj[$e->from_id][] = [$e->to_id, $w];
            if ($e->bidirectional) {
                $adj[$e->to_id][] = [$e->from_id, $w];
            }
        }
        return [$nodes, $adj];
    }

    private function euclid($x1,$y1,$x2,$y2): float
    {
        $dx = $x1 - $x2; $dy = $y1 - $y2;
        return sqrt($dx*$dx + $dy*$dy);
    }

    private function dijkstra(array $adj, int $start, int $goal): array
    {
        $dist  = []; $prev = []; $q = [];
        foreach ($adj as $u => $_) {
            $dist[$u] = INF;
            $prev[$u] = null;
            $q[$u]    = true;
        }
        // ensure start exists even if it has no outgoing edges
        $dist[$start] = 0; $q[$start] = true;

        while (!empty($q)) {
            // pick u in q with smallest dist
            $u = null; $min = INF;
            foreach ($q as $id => $_) {
                if (isset($dist[$id]) && $dist[$id] < $min) { $min = $dist[$id]; $u = $id; }
            }
            if ($u === null) break;
            unset($q[$u]);

            if ($u === $goal) break; // done

            if (!isset($adj[$u])) continue;
            foreach ($adj[$u] as [$v, $w]) {
                $alt = $dist[$u] + $w;
                if (!isset($dist[$v]) || $alt < $dist[$v]) {
                    $dist[$v] = $alt;
                    $prev[$v] = $u;
                    $q[$v] = true; // ensure v is in queue
                }
            }
        }

        // reconstruct
        $path = [];
        $u = $goal;
        if (!isset($prev[$u]) && $u !== $start) {
            return [$start]; // no path found
        }
        while ($u !== null) {
            array_unshift($path, $u);
            $u = $prev[$u] ?? null;
        }
        return $path;
    }

    public function coords(string $slug)
    {
        $floor = \DB::table('floors')->where('slug', $slug)->first();
        abort_unless($floor, 404);

        $checkpoints = \DB::table('checkpoints')
            ->where('floor_id', $floor->id)
            ->orderBy('name')
            ->get(['id','name','slug','x','y','type']);

        return view('map.coords', [
            'floor' => $floor,
            'checkpoints' => $checkpoints,
            // suggestions you’ll see in the name field (edit as you like)
            'suggestions' => match ($slug) {
                'groundfloor' => ['Natural Resources & Economy Gallery'],
                'floor1'      => ['Entrance (F1)','Textile Gallery','Temporary Gallery','Islamic Gallery','Petroleum Gallery','Junction near Information'],
                'floor2'      => ['Craft Gallery','Royal Gallery'],
                'floor3'      => ['History Gallery','Bridge'],
                default       => [],
            },
        ]);
    }


}