<?php if (! defined('BASEPATH')) exit('No direct script access allowed');


class Staticmap extends CI_Controller {

    public function render($slug = '') {

        // set to true to remove cached imaged for debugging pruposes
        $debugging = false;
        if (ENVIRONMENT == 'development') {
            $debugging = true;
        }

        $this->load->model('staticmap_model');
        $this->load->model('stationsetup_model');
        $this->load->model('visitor_model');

        $slug = $this->security->xss_clean($slug);
        if (empty($slug)) {
            show_404(__("Unknown Public Page."));
        }
        // check if the public slug exists
        $logbook_id = $this->stationsetup_model->public_slug_exists_logbook_id($slug);
        if ($logbook_id == false) {
            show_404(__("Unknown Public Page."));
        }

        // Optional override-parameters
        $band = strtolower($this->input->get('band', TRUE) ?? '');
        if ($band === 'sat') {
            $band = 'SAT'; // we need to uppercase SAT for the query, but for the cache key we want it lowercase as all other bands are lowercase
        } elseif ($band !== 'nbf' && !in_array($band, $this->config->item('bands_available') ?? [], true)) {
            $band = 'nbf';
        }

        // Orbit: only LEO/MEO/GEO are meaningful, anything else means "no orbit"
        $orbit = strtoupper($this->input->get('orbit', TRUE) ?? '');
        if (!in_array($orbit, ['LEO', 'MEO', 'GEO'], true)) {
            $orbit = 'nOrb';
        }

        // Continent: fixed set of seven continents, else "no continent"
        $continent = strtoupper($this->input->get('continent', TRUE) ?? '');
        if (!in_array($continent, ['AF', 'AN', 'AS', 'EU', 'NA', 'OC', 'SA'], true)) {
            $continent = 'nC';
        }

        $thememode = ($this->input->get('theme', TRUE) ?? '') == '' ? '' : strtolower($this->input->get('theme', TRUE));
        $hide_home = $this->input->get('hide_home', TRUE) == 1 ? true : false;

        // Contest: validate against the known contest adifnames (cached for 24h)
        $contest = strtoupper($this->input->get('contest', TRUE) ?? '');
        if ($contest != '') {
            $this->load->driver('cache', [
                'adapter' => $this->config->item('cache_adapter') ?? 'file',
                'backup' => $this->config->item('cache_backup') ?? 'file',
                'key_prefix' => $this->config->item('cache_key_prefix') ?? ''
            ]);
            if (!$valid_contests = $this->cache->get('valid_contests')) {
                $this->load->model('contesting_model');
                $valid_contests = array_map('strtoupper', array_filter(array_column($this->contesting_model->getAllContests(), 'adifname')));
                $this->cache->save('valid_contests', $valid_contests, 60 * 60 * 24);
            }
            if (!in_array($contest, $valid_contests, true)) {
                $contest = 'nContest';
            }
        } else {
            $contest = 'nContest';
        }

        // Date range: normalize to canonical Y-m-d and clamp to a 80-year sanity
        // window. Garbage or out-of-range values collapse onto the no-date default,
        // so they all share a single cache key instead of one file per input.
        $minDate = strtotime('-80 years');
        $ts = strtotime($this->input->get('start_date', TRUE) ?? '');
        $start_date = ($ts === false || $ts < $minDate || $ts > time()) ? 'noStart' : date('Y-m-d', $ts);
        $ts = strtotime($this->input->get('end_date', TRUE) ?? '');
        $end_date = ($ts === false || $ts < $minDate || $ts > time()) ? 'noEnd' : date('Y-m-d', $ts);

        // Day: only today/yesterday are handled by the query, else "no day filter"
        $day = $this->input->get('day', TRUE) ?? '';
        if (!in_array($day, ['today', 'yesterday'], true)) {
            $day = '';
        }

        // if the user defines an Satellite Orbit, we need to set the band to SAT
        if ($orbit != 'nOrb') {
            $band = 'SAT';
        }

        /**
         * Based on Export Settings -> Overlays and QSO Count
         */
        // qsocount
        $qsocount = $this->input->get('qsocount', TRUE) ?? '';
        // if the qso count is not a number, set it to the user option or 250 per default (same as used in stationsetup)
        $uid = $this->stationsetup_model->getContainer($logbook_id, false)->row()->user_id;
        if ($qsocount == 0 || (!is_numeric($qsocount) && $qsocount != 'all')) {
            $qsocount = $this->user_options_model->get_options('ExportMapOptions', array('option_name' => 'qsocount', 'option_key' => $slug), $uid)->row()->option_value ?? 250;
        }

        // Night shadow
        $night_shadow = $this->input->get('ns', TRUE) ?? '';
        if ($night_shadow == '' || ($night_shadow != 1 && $night_shadow != 0)) {
            $r = $this->user_options_model->get_options('ExportMapOptions', array('option_name' => 'nightshadow_layer', 'option_key' => $slug), $uid)->row()->option_value ?? '';
            $night_shadow = $r == 'true' ? true : false;
        }

        // Pathlines
        $pathlines = $this->input->get('pl', TRUE) ?? '';
        if ($pathlines == '' || ($pathlines != 1 && $pathlines != 0)) {
            $r = $this->user_options_model->get_options('ExportMapOptions', array('option_name' => 'path_lines', 'option_key' => $slug), $uid)->row()->option_value ?? '';
            $pathlines = $r == 'true' ? true : false;
        }

        // CQ Zones
        $cqzones = $this->input->get('cqz', TRUE) ?? '';
        if ($cqzones == '' || ($cqzones != 1 && $cqzones != 0)) {
            $r = $this->user_options_model->get_options('ExportMapOptions', array('option_name' => 'cqzones_layer', 'option_key' => $slug), $uid)->row()->option_value ?? '';
            $cqzones = $r == 'true' ? true : false;
        }

        // ITU Zones
        $ituzones = $this->input->get('ituz', TRUE) ?? '';
        if ($ituzones == '' || ($ituzones != 1 && $ituzones != 0)) {
            $r = $this->user_options_model->get_options('ExportMapOptions', array('option_name' => 'ituzones_layer', 'option_key' => $slug), $uid)->row()->option_value ?? '';
            $ituzones = $r == 'true' ? true : false;
        }

        // Custom map center (gridsquare) for the static map, if provided by the user.
        $this->load->is_loaded('Qra') ?: $this->load->library('Qra');
        $custom_center = strtoupper($this->input->get('center', TRUE) ?? '');
        if (strlen($custom_center) < 4 || !$this->qra->validate_grid($custom_center, 'grid')) {
            $custom_center = NULL;
        }

        // Watermark
        $watermark = $this->input->get('wm', TRUE) ?? '';
        if ($watermark == '' || ($watermark != 1 && $watermark != 0)) {
            $watermark = true;
        }

        // handling the theme mode
        $this->load->model('themes_model');
        if ($thememode == null || $thememode == '' || ($thememode != 'dark' && $thememode != 'light')) {
            $r =  $this->themes_model->get_theme_mode($this->optionslib->get_option('option_theme'));
            $thememode = $r;
        }

        // prepare the cache directory
        $cachepath = $this->config->item('cache_path') == '' ? APPPATH . 'cache/' : $this->config->item('cache_path');
        $cacheDir = $cachepath . "staticmap_images/";
        if (!is_dir($cacheDir)) {
            mkdir($cacheDir, 0755, true);
        }

        // we need the realpath later for validation
        $cacheDir = realpath($cachepath . "staticmap_images/");

        // create a unique filename for the cacheund e
        $filenameRaw = $uid
                     . $logbook_id
                     . $qsocount
                     . $band
                     . $thememode
                     . $continent
                     . $hide_home
                     . ($night_shadow == false ? 0 : 1)
                     . ($pathlines == false ? 0 : 1)
                     . ($cqzones == false ? 0 : 1)
                     . ($ituzones == false ? 0 : 1)
                     . $orbit
                     . $contest
                     . $start_date
                     . $end_date
                     . $day
                     . $watermark
                     . ($custom_center ?? '');

        $filename = crc32('staticmap_' . $slug) . '_' . substr(md5($filenameRaw), 0, 12) . '.png';
        $filepath = $cacheDir . '/' . $filename;

        // Set the cache time to 7 days
        $maxAge = 3600 * 24 * 7;

        // remove the cached image for debugging purposes
        if ($debugging) {
            if (is_file($filepath)) {
                unlink($filepath);
            }
        }

        if ($this->staticmap_model->validate_cached_image($filepath, $cacheDir, $maxAge, $slug)) {
            log_message('debug', 'Static map image found in cache: ' . $filename);
            header('Content-Type: image/png');
            readfile($filepath);
            return;
        } else {
            if (in_array('gd', get_loaded_extensions())) {

                if ($logbook_id != false) {
                    // Get associated station locations for mysql queries
                    $logbooks_locations_array = $this->stationsetup_model->get_container_relations($logbook_id);

                    if (!$logbooks_locations_array) {
                        show_404(__("Empty Logbook"));
                    }
                } else {
                    log_message('error', $slug . ' has no associated station locations');
                    show_404(__("Unknown Public Page."));
                    die;
                }

                // we need to get an array of all coordinates of the stations
                if (!$this->load->is_loaded('logbook_model')) {
                    $this->load->model('logbook_model');
                }

                $grids = [];
                foreach ($logbooks_locations_array as $location) {
                    $station_info = $this->logbook_model->check_station($location);
                    if ($station_info) {
                        $grids[] = $station_info['station_gridsquare'];
                    }
                }
                
                $coordinates = [];
                foreach ($grids as $grid) {
                    $coordinates[] = $this->qra->qra2latlong($grid);
                }

                if ($custom_center) {
                    $centerMap = $this->qra->qra2latlong($custom_center);
                } else {
                    $centerMap = $this->qra->getCenterLatLng($coordinates);
                }

                $qsos = $this->visitor_model->get_qsos(
                    $qsocount, 
                    $logbooks_locations_array, 
                    $band == 'nbf' ? '' : $band, 
                    $continent == 'nC' ? '' : $continent, 
                    $orbit == 'nOrb' ? '' : $orbit, 
                    $contest == 'nContest' ? '' : $contest,
                    $start_date == 'noStart' ? '' : $start_date,
                    $end_date == 'noEnd' ? '' : $end_date,
                    $day
                );

                $image = $this->staticmap_model->render_static_map($qsos, $uid, $centerMap, $coordinates, $filepath, $continent, $thememode, $hide_home, $night_shadow, $pathlines, $cqzones, $ituzones, $watermark);

                header('Content-Type: image/png');

                if ($image == false) {
                    $msg = "Can't create static map image. Something went wrong.";
                    log_message('error', $msg);
                    show_404($msg);
                } else {
                    readfile($filepath);
                }
            } else {
                $msg = "Can't create static map image. Extention 'php-gd' is not installed. Install it and restart the webserver.";
                log_message('error', $msg);
                echo $msg;
            }
        }

        log_message('debug', 'Static map image generator took ' . round((memory_get_peak_usage() / 1024 / 1024)) . " MB of memory");
    }
}
