// This file is part of Screenshot Location and Metadata.
//
// Screenshot Location and Metadata is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// Screenshot Location and Metadata is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// Screenshot Location and Metadata. If not, see https://www.gnu.org/licenses/.

using System.Linq;
using NetScriptFramework.SkyrimSE;

namespace ScreenshotLocationAndMetadata
{
    static class MapNameHelper
    {
        /// <summary>
        /// Attempts to get the cell name as displayed on the map, load doors, and save games. For interior cells, this
        /// is the cell name itself. For exterior worldspace cells, the game seems to look at all of the cell's Regions
        /// for the Region Data Entry of type Map with the highest Priority and uses its Map Name. The Override flag
        /// seems to be irrelevant. The order of Regions also does not matter (xEdit simply sorts them by form id).
        ///
        /// <para />
        ///
        /// For most places this ends up being BorderRegionSkyrim which has a Map entry named "Skyrim". With Unique
        /// Region Names installed, hold-specific Regions are added to every worldspace cell that override the former
        /// with names like "Haafingar" or "Whiterun Hold".
        ///
        /// <para />
        ///
        /// Note that this is different from using CurrentLocation, as that refers to the Location record which serves
        /// other purposes. As an example, if that were used, the Winterhold "Hall of Countenance" would become
        /// "Dormitory" (that being the name of its LCTN record). On the other hand, KatlasFarmExterior (the Solitude
        /// stables) has the Location "Katla's Farm," while a save game in that cell, and the door leaving the stables
        /// building, simply says "Haafingar" (or "Skyrim").
        /// </summary>
        /// <remarks>
        /// The local map appears to always show the worldspace name when outside (always "Skyrim"), not the region name
        /// even with Unique Region Names. For outside saves to use the region name, Regional Save Names is required, as
        /// apparently the naming behavior was changed in SSE.
        /// </remarks>
        /// <param name="cell">The cell for which to get the appropriate map name.</param>
        public static string GetMapName(TESObjectCELL cell)
        {
            if (cell is null)
            {
                return null;
            }

            // For interior cells, the name used is simply the cell name
            if (cell.IsInterior)
            {
                return cell.Name;
            }

            // Look for the cell record's list of Regions
            TESRegionList regionList = FindRegionList(cell.ExtraDataList);
            if (regionList is null)
            {
                return cell.Name;
            }

            // Find the Map data with the highest priority
            TESRegionDataMap mapData = regionList
                .SelectMany(r => MemoryLinkedList<TESRegionData>.FromAddress(r.DataList))
                .OfType<TESRegionDataMap>()
                .OrderBy(d => d.Header.Priority)
                .LastOrDefault();

            // Return its map name, or fall back again to cell name if there are no regions with map data
            return mapData?.MapName.Text ?? cell.Name;
        }

        /// <summary>
        /// Searches the ExtraDataList for the list of Regions. For some reason this isn't just a property on
        /// TESObjectCell but hidden within a linked list of various data types which we need to traverse by hand.
        /// </summary>
        /// <param name="linkedList">The <see cref="TESObjectCELL.ExtraDataList"/>.</param>
        /// <returns>The <see cref="TESRegionList"/>, or null if none was found.</returns>
        private static TESRegionList FindRegionList(BSExtraDataList linkedList)
        {
            var item = linkedList.First;

            while (!(item is null))
            {
                if (item is ExtraRegionList regionListItem)
                {
                    return regionListItem.RegionList;
                }

                item = item.Next;
            }

            return null;
        }
    }
}