/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */
using UnityEngine;
namespace Live2D.Cubism.Rendering.Masking
{
    /// 
    /// Virtual pool allocator for s.
    /// 
    internal sealed class CubismMaskTilePool
    {
        /// 
        /// Level of subdivisions.
        /// 
        private int Subdivisions { get; set; }
        /// 
        /// Pool slots.
        /// 
        /// 
        ///  slots are in use,  are available slots.
        /// 
        private bool[] Slots { get; set; }
        #region Ctors
        /// 
        /// Initializes instance.
        /// 
        /// Number of  subdivisions.
        /// Number of  color channels.
        public CubismMaskTilePool(int subdivisions, int channels)
        {
            Subdivisions = subdivisions;
            Slots = new bool[(int)Mathf.Pow(4, subdivisions) * channels];
        }
        #endregion
        /// 
        /// Acquires tiles.
        /// 
        /// Number of tiles to acquire.
        /// Acquired tiles on success;  otherwise.
        public CubismMaskTile[] AcquireTiles(int count)
        {
            var result = new CubismMaskTile[count];
            // Populate container.
            for (var i = 0; i < count; ++i)
            {
                var allocationSuccessful = false;
                for (var j = 0; j < Slots.Length; ++j)
                {
                    // Skip occupied slots.
                    if (Slots[j])
                    {
                        continue;
                    }
                    // Generate tile.
                    result[i] = ToTile(j);
                    // Flag slot as occupied.
                    Slots[j] = true;
                    // Flag allocation as successful.
                    allocationSuccessful = true;
                    break;
                }
                // Return as soon as one allocation fails.
                if (!allocationSuccessful)
                {
                    return null;
                }
            }
            // Return on success.
            return result;
        }
        /// 
        /// Releases tiles.
        /// 
        /// Tiles to release.
        public void ReturnTiles(CubismMaskTile[] tiles)
        {
            // Flag slots as available.
            for (var i = 0; i < tiles.Length; ++i)
            {
                Slots[ToIndex(tiles[i])] = false;
            }
        }
        /// 
        /// Converts from index to .
        /// 
        /// Index to convert.
        /// Mask tile matching index.
        private CubismMaskTile ToTile(int index)
        {
            var tileCounts = (int)Mathf.Pow(4, Subdivisions - 1);
            var tilesPerRow = (int)Mathf.Pow(2, Subdivisions - 1);
            var tileSize = 1f / (float)tilesPerRow;
            var channel = index / tileCounts;
            var currentTilePosition = index - (channel * tileCounts);
            var column = currentTilePosition / tilesPerRow;
            var rowId = currentTilePosition % tilesPerRow;
            return new CubismMaskTile
            {
                Channel = channel,
                Column = column,
                Row = rowId,
                Size = tileSize
            };
        }
        /// 
        /// Converts from  to index.
        /// 
        /// Tile to convert.
        /// Tile index.
        private int ToIndex(CubismMaskTile tile)
        {
            var tileCounts = (int)Mathf.Pow(4, Subdivisions - 1);
            var tilesPerRow = (int)Mathf.Pow(2, Subdivisions - 1);
            return (int)((tile.Channel * tileCounts) + (tile.Column * tilesPerRow) + tile.Row);
        }
    }
}