<?php

class Station extends Model
{
    public function __construct($id)
    {
        parent::__construct($id);
    }

    /**
     * Returnes likly ham radio callsign
     *
     * @return string
     */
    public function getLiklyHamRadioCallsign()
    {
        if ($this->sourceId == 1 && $this->stationTypeId == 1) {
            $pos = strpos($this->name, '-');
            if ($pos !== false) {
                $callsign = substr($this->name, 0, $pos);
            } else {
                $callsign = $this->name;
            }

            if (strlen($callsign) >= 3 && preg_match('~[0-9]~', substr($callsign, 1, strlen($callsign)-1))) {
                // At least 3 letters and a digit somewhere in the middle
                return $callsign;
            }
        }

        return null;
    }

    /**
     * Get the latest used comment for this station
     *
     * @param  int $packetType  (1 = comment)
     * @return array
     */
    public function getLatestComment($packetType = 1)
    {
        static $result = null;

        // Check cache to prevent multiple queries and return if it's not empty
        if ($result != null) return $result;

        if ($this->latestConfirmedPacketTimestamp > (time() - (60*60*24))) {
            // Latest packet is not that old, go on

            $sql = 'select timestamp, comment from packet where station_id = ? and timestamp > ? and packet_type_id = ? and comment is not null order by timestamp desc limit 1';
            $stmt = PDOConnection::getInstance()->prepareAndExec($sql, [$this->id, $this->latestConfirmedPacketTimestamp - (60*60*24), $packetType]);

            $result = $stmt->fetch(PDO::FETCH_OBJ);

            // Trim off leading garbage for any improperly stored packets
            if (isset($result->comment)) $result->comment = ltrim($result->comment, '`');
        }

        return $result;
    }


    /**
     * Get the latest beacon comment for this station
     *
     * @return array
     */
    public function getLatestBeacon()
    {
        static $result = null;

        // Check cache to prevent multiple queries and return if it's not empty
        if ($result != null) return $result;

        if ($this->latestConfirmedPacketTimestamp > (time() - (60*60*24)))
        {
            // Query the beacon receiver id
            $sql = "SELECT id FROM receiver WHERE name = 'BEACON' LIMIT 1";
            $stmt = PDOConnection::getInstance()->prepareAndExec($sql);
            $beaconReceiverId = $stmt->fetchColumn() ?: 0;

            // Latest packet is not that old, go on
            if ($beaconReceiverId > 0)
            {
              $sql = 'select id as packet_id, timestamp, comment from packet where station_id = ? and timestamp > ? and receiver_id = ? and comment is not null order by timestamp desc limit 1';
              $stmt = PDOConnection::getInstance()->prepareAndExec($sql, [$this->id, $this->latestConfirmedPacketTimestamp - (60*60*24), $beaconReceiverId]);
              $result = $stmt->fetch(PDO::FETCH_OBJ);

              if ($result == false) return null;

              // Trim off leading garbage for any improperly stored packets
              if (isset($result->comment)) $result->comment = ltrim($result->comment, '`');
            }
        }

        return $result;
    }


    /**
     * Returns the station type name
     *
     * @return string
     */
    public function getStationTypeName()
    {
       switch ($this->stationTypeId)
       {
          case 1:
              return 'Station';
          case 2:
              return 'Object';
          case 3:
              return 'Item';
        }
        return 'Unknown';
    }


    /**
     * Returns OGN sender address
     *
     * @return string
     */
    public function getOgnSenderAddress()
    {
        if (isset($this->latestOgnSenderAddress) && $this->latestOgnSenderAddress != '') {
            return $this->latestOgnSenderAddress;
        }
        return null;
    }

    /**
     * Returns OGN device
     *
     * @return Model
     */
    public function getOgnDevice()
    {
        static $cache = array();
        $key = $this->id;
        if (!isset($cache[$key])) {
            if (isset($this->latestOgnSenderAddress) && $this->latestOgnSenderAddress != '') {
                $cache[$key] = ModelRepository::getInstance()->getObjectFromSql('select * from ogn_device where device_id = ?', [$this->latestOgnSenderAddress]);
            } else {
                $cache[$key] = null;
            }
        }
        return $cache[$key];
    }

    /**
     * Returns OGN device DB aircraft type name
     *
     * @return string
     */
    public function getOgnDdbAircraftTypeName()
    {
        $ognDevice = $this->getOgnDevice();
        if ($ognDevice) {
             switch ($ognDevice->ddbAircraftType) {
            case 1:
                return 'Glider/Motoglider';
            case 2:
                return 'Plane';
            case 3:
                return 'Ultralight';
            case 4:
                return 'Helicopter';
            case 5:
                return 'Drone/UAV';
            case 6:
                return 'Other';
             }
        }
        return null;
    }

    /**
     * Returns OGN aircraft type name
     *
     * @return string
     */
    public function getOgnAircraftTypeName()
    {
        if (isset($this->latestOgnAircraftTypeId) && $this->latestOgnAircraftTypeId != '') {
            switch ($this->latestOgnAircraftTypeId) {
            case 1:
                return 'Glider';
            case 2:
                return 'Tow Plane';
            case 3:
                return 'Helicopter';
            case 4:
                return 'Parachute';
            case 5:
                return 'Drop Plane';
            case 6:
                return 'Hang Glider';
            case 7:
                return 'Para Glider';
            case 8:
                return 'Powered Aircraft';
            case 9:
                return 'Jet Aircraft';
            case 10:
                return 'UFO';
            case 11:
                return 'Balloon';
            case 12:
                return 'Airship';
            case 13:
                return 'UAV';
            case 14:
                return '';
            case 15:
                return 'Static Object';
            }
        }
        return null;
    }

    /**
     * Returns source description
     *
     * @return string
     */
    public function getSourceDescription()
    {
        if (isset($this->sourceId) && $this->sourceId != '') {
            if ($this->sourceId == 1) {
                return '<a target="_blank" rel="nofollow" href="http://www.aprs-is.net/">APRS-IS</a>';
            } elseif ($this->sourceId == 2) {
                return '<a target="_blank" rel="nofollow" href="http://wxqa.com/">CWOP (Citizen Weather Observer Program)</a>';
            } elseif ($this->sourceId == 3) {
                return '<a target="_blank" rel="nofollow" href="http://www.cbaprs.de">CBAPRS (Citizen Band APRS)</a>';
            } elseif ($this->sourceId == 5) {
                return '<a target="_blank" rel="nofollow" href="http://wiki.glidernet.org/">OGN (Open Glider Network)</a>';
            }
        }
        return null;
    }

    /**
     * Get distance between specified lat/lng and the station latest position (confirmed position if exists)
     *
     * @param  float $lat
     * @param  float $lng
     * @return int;
     */
    public function getDistance($lat, $lng)
    {
        static $cache = array();
        $key = $this->id . ':' . $lat . ':' . $lng;
        if (!isset($cache[$key])) {

            $latestLatitude = null;
            $latestLongitude = null;
            if ($this->latestConfirmedLongitude !== null && $this->latestConfirmedLatitude !== null) {
                $latestLatitude = $this->latestConfirmedLatitude;
                $latestLongitude = $this->latestConfirmedLongitude;
            } else if ($this->latestLongitude !== null && $this->latestLatitude !== null) {
                $latestLatitude = $this->latestLatitude;
                $latestLongitude = $this->latestLongitude;
            }

            if ($lat !== null && $lng !== null && $latestLatitude !== null && $latestLongitude !== null) {
                $theta = $lng - $latestLongitude;
                $dist = sin(deg2rad($lat)) * sin(deg2rad($latestLatitude)) +  cos(deg2rad($lat)) * cos(deg2rad($latestLatitude)) * cos(deg2rad($theta));
                $dist = acos($dist);
                $dist = rad2deg($dist);
                $miles = $dist * 60 * 1.1515;
                $cache[$key] = round($miles * 1609.344, 0);
            } else {
                $cache[$key] = null;
            }
        }
        return $cache[$key];
    }

    /**
     * Get the latest maindenhead locator (grid aquare) using latest position (confirmed position if exists)
     *
     * @param  float $precision - Number of coordinate pairs to return; minimum 1 ("FN"), maximum 5 ("FN13WB43JL"), default 5.
     *
     * @return string;
     */
    public function getMaidenheadLocator($precision = 5)
    {
        $latestLatitude = null;
        $latestLongitude = null;
        if ($this->latestConfirmedLongitude !== null && $this->latestConfirmedLatitude !== null) {
            $latestLatitude = $this->latestConfirmedLatitude;
            $latestLongitude = $this->latestConfirmedLongitude;
        } else if ($this->latestLongitude !== null && $this->latestLatitude !== null) {
            $latestLatitude = $this->latestLatitude;
            $latestLongitude = $this->latestLongitude;
        }

        if ($latestLatitude !== null && $latestLongitude !== null) {
          if ($latestLongitude >= 180 || $latestLongitude <= -180 || $latestLatitude >= 90 || $latestLatitude <= -90) {
            return "Invalid";
          }

          return getMaidenheadFromCoordinates($latestLatitude, $latestLongitude, $precision);
        } else {
          return 'Unknown';
        }
    }

    /**
     * Returnes icon http path
     *
     * @param  int $scaleWidth
     * @param  int $scaleHeight
     * @return string
     */
    public function getIconFilePath($scaleWidth = null, $scaleHeight = null)
    {
        if (strlen($this->latestConfirmedSymbol ?? '') >= 1 && strlen($this->latestConfirmedSymbolTable ?? '') >= 1) {
            $symbolAsciiValue = ord(substr($this->latestConfirmedSymbol, 0, 1));
            $symbolTableAsciiValue = ord(substr($this->latestConfirmedSymbolTable, 0, 1));
        } else {
            // Default values
            $symbolAsciiValue = 125;
            $symbolTableAsciiValue = 47;
        }

        $scaleStrValue = '';
        if ($scaleWidth !== null && $scaleHeight !== null) {
            $scaleStrValue = '-scale' . $scaleWidth . 'x' . $scaleHeight;
        }

        return '/symbols/symbol-' . $symbolAsciiValue . '-' . $symbolTableAsciiValue . $scaleStrValue . '.svg';
    }

    /**
     * Get array of the latest used symbols for this station
     *
     * @param  int $scaleWidth
     * @param  int $scaleHeight
     * @return array
     */
    public function getLatestIconFilePaths($scaleWidth = 24, $scaleHeight = 24)
    {
        $result = Array();
        if ($this->latestConfirmedPacketTimestamp > (time() - (60*60*24))) {
            // Latest packet is not that old, go on

            $scaleStrValue = '';
            if ($scaleWidth !== null && $scaleHeight !== null) {
                $scaleStrValue = '-scale' . $scaleWidth . 'x' . $scaleHeight;
            }

            $sql = 'select symbol, symbol_table from packet where station_id = ? and timestamp > ? group by symbol, symbol_table';
            $stmt = PDOConnection::getInstance()->prepareAndExec($sql, [$this->id, $this->latestConfirmedPacketTimestamp - (60*60*24)]);

            $records = $stmt->fetchAll(PDO::FETCH_ASSOC);
            foreach ($records as $record) {

                if (strlen($record['symbol'] ?? '') >= 1 && strlen($record['symbol_table'] ?? '') >= 1) {
                    $key = $record['symbol'] . ':' . $record['symbol_table'];

                    $symbolAsciiValue = ord(substr($record['symbol'], 0, 1));
                    $symbolTableAsciiValue = ord(substr($record['symbol_table'], 0, 1));
                    $result[$key] = '/symbols/symbol-' . $symbolAsciiValue . '-' . $symbolTableAsciiValue . $scaleStrValue . '.svg';
                }
            }
        }

        return array_values($result);
    }

    /**
     * Get packet frequency in number of seconds for the latest 10 packets
     *
     * @param  string $date
     * @param  int    &$numberOfPackets
     * @return int
     */
    public function getPacketFrequency($date, &$numberOfPackets)
    {
        $pdo = PDOConnection::getInstance();
        if ($this->latestPacketTimestamp !== null) {
            $timestamp = $this->latestPacketTimestamp;
        } else {
            return null;
        }

        // Find start timestamp
        $sql = 'select timestamp ts from packet where station_id = ? and map_id in (1,2,5,7,9) and timestamp < ? order by timestamp desc limit 1';
        $stmt = $pdo->prepareAndExec($sql, [$this->id, $timestamp - 1]);

        $record = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!empty($record) && $record['ts'] != null && $record['ts'] < $timestamp) {
            $startTimestamp = $timestamp - (($timestamp - $record['ts'])*10);
            if ($timestamp - $startTimestamp < 600) {
                $startTimestamp = $timestamp - 600;
            }
        } else {
            if ($date === null) {
                // Try to find frequency for the date before
                $date = time();
                return $this->getPacketFrequency($date, $numberOfPackets);
            } else {
                // Give up
                return null;
            }
        }

        $sql = 'select (max(timestamp) - min(timestamp)) / count(*) freq, count(*) c from packet  where station_id = ? and map_id in (1,2,5,7,9) and timestamp >= ?';
        $stmt = $pdo->prepareAndExec($sql, [$this->id, $startTimestamp]);
        $record = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!empty($record) && $record['freq'] > 0) {
            $numberOfPackets = $record['c'];
            return $record['freq'];
        } else {
            return null;
        }
    }

    /**
     * Get total packets recorded for the station
     *
     * @return int
     */
    public function getTotalPackets()
    {
        $pdo = PDOConnection::getInstance();

        $sql = 'select count(*) c from packet where station_id = ?';
        $stmt = $pdo->prepareAndExec($sql, [$this->id]);
        $record = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!empty($record) && $record['c'] > 0) {
            return $record['c'];
        } else {
            return null;
        }
    }


    /**
     * Get total packets recorded for the station
     *
     * @return int
     */
    public function getTotalPositions()
    {
        $pdo = PDOConnection::getInstance();

        $sql = 'select count(*) c from packet_path where sending_station_id = ?';
        $stmt = $pdo->prepareAndExec($sql, [$this->id]);
        $record = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!empty($record) && $record['c'] > 0) {
            return $record['c'];
        } else {
            return null;
        }
    }


    /**
     * Gets the current value of the view (hit) counter for the station
     *
     * @return int
     */
    public function getViews()
    {
        $pdo = PDOConnection::getInstance();

        $sql = 'SELECT view_count FROM station_views WHERE station_id = ?';
        $stmt = $pdo->prepareAndExec($sql, [$this->id]);
        $this->viewCount = intval($stmt->fetchColumn());
        return $this->viewCount;
    }


    /**
     * Increments the view (hit) counter table record (inserting if it doesn't exist) for the station by one and returns the current views
     *
     * @return int
     */
    public function incrementViews()
    {
        $pdo = PDOWriterConnection::getInstance();

        $sql = 'INSERT INTO station_views (station_id, view_count) VALUES (?, 1) ON CONFLICT (station_id) DO UPDATE SET view_count = (SELECT view_count + 1 FROM station_views WHERE station_id = ?) RETURNING view_count;';
        $stmt = $pdo->prepareAndExec($sql, [$this->id, $this->id]);
        $this->viewCount = intval($stmt->fetchColumn());
        return $this->viewCount;
    }


    /*
     * Returns symbol description
     * @param boolean $includeUndefinedOverlay
     * @return string
     */
    public function getLatestSymbolDescription($includeUndefinedOverlay = true)
    {
        $symbol = null;
        $symbolTable = null;

        if (strlen($this->latestConfirmedSymbol ?? '') >= 1 && strlen($this->latestConfirmedSymbolTable ?? '') >= 1) {
            $symbol = $this->latestConfirmedSymbol;
            $symbolTable = $this->latestConfirmedSymbolTable;
        }

        return getSymbolDescription($symbolTable, $symbol, $includeUndefinedOverlay);
    }

    /**
     * Get the list of #APRSThursday checkin dates for the station
     *
     * @param  int $limit (Limits number )
     * @return array (false on no records found)
     */
    public function getAPRSThursdayCheckins($limit = 1)
    {
        $pdo = PDOConnection::getInstance();

        $sql = 'SELECT * FROM aprs_thursday WHERE station_id = ? ORDER BY net_date DESC LIMIT ?';
        $stmt = $pdo->prepareAndExec($sql, [$this->id, $limit]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }


    /**
     * Get the list of #APRSThursday checkin dates for the station and related SSIDs
     *
     * @param  int $limit (Limits number )
     * @return array (false on no records found)
     */
    public function getRelatedAPRSThursdayCheckins($limit = 52)
    {
        // Fetch relates stations
        $call = $this->getLiklyHamRadioCallsign();
        $stations = [];
        $relatedStations = StationRepository::getInstance()->getRelatedObjectListByStationId($this->id, 15);
        foreach($relatedStations AS $station)
        {
          $stations[] = $station->id;
        }
        $station_list = implode(',', $stations);
        $pdo = PDOConnection::getInstance();

        $sql = "SELECT * FROM aprs_thursday WHERE station_id IN(SELECT id FROM station WHERE name ILIKE ?) ORDER BY net_date DESC LIMIT ?";
        $stmt = $pdo->prepare($sql);
        $stmt->bindValue(1, "$call%", PDO::PARAM_STR);
        $stmt->bindValue(2, $limit);
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }


    /**
     * Get all of the DMR ID's for the station
     *
     * @param  int $limit (Limits number )
     * @return array (false on no records found)
     */
    public function getDMRIDs()
    {
      $dmrIdList = DMRDatabaseRepository::getInstance()->getObjectListByCallsign($this->getLiklyHamRadioCallsign());
      return $dmrIdList;
    }

    /**
     * Get all of the FCC File for the station
     *
     * @return FCCDatabase (or empty, intitialized object if none found)
     */
    public function getFCCFile()
    {
        $fccFile = FCCDatabaseRepository::getInstance()->getObjectByCallsign($this->getLiklyHamRadioCallsign());
        return $fccFile;
    }

    /**
     * Get all of the Canadian License File for the station
     *
     * @return CADatabase (or empty, intitialized object if none found)
     */
    public function getCAFile()
    {
        $caFile = CADatabaseRepository::getInstance()->getObjectByCallsign($this->getLiklyHamRadioCallsign());
        return $caFile;
    }

    /**
     * Check if the station has had invalid packets within the past week.
     *
     * @return int (number of invalid packets, or 0 if none found)
     */
    public function getInvalidPackets()
    {
        $invalid = PacketRepository::getInstance()->getNumberOfInvalidPacketsByStationId($this->id);
        return $invalid;
    }
}
