__license__ = """
NML 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 2 of the License, or
(at your option) any later version.

NML 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 NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""

from nml import generic
from nml.actions import base_action, real_sprite


class Action5(base_action.BaseAction):
    def __init__(self, type, num_sprites, offset):
        self.type = type
        self.num_sprites = num_sprites
        self.offset = offset

    def prepare_output(self, sprite_num):
        if self.offset is not None:
            self.type |= 0x80

    def write(self, file):
        # <Sprite-number> * <Length> 05 <type> <num-sprites> [<offset>]
        size = 5 if self.offset is None else 8
        file.start_sprite(size)
        file.print_bytex(0x05)
        file.print_bytex(self.type)
        file.print_bytex(0xFF)
        file.print_word(self.num_sprites)
        if self.offset is not None:
            file.print_bytex(0xFF)
            file.print_word(self.offset)
        file.newline()
        file.end_sprite()

    def skip_action7(self):
        # skipping with Action7 should work, according to the Action7/9 specs
        # However, skipping invalid (OpenTTD-only) Action5s in TTDP can only be done using Action9, else an error occurs
        # To be on the safe side, don't allow skipping with Action7 at all
        return False


class Action5BlockType:
    FIXED = (0,)  # fixed number of sprites
    ANY = (1,)  # any number of sprites
    OFFSET = (2,)  # flexible number of sprites, offset may be set


action5_table = {
    "PRE_SIGNAL": (0x04, 48, Action5BlockType.OFFSET),  # deprecated, use "SIGNALS" in all cases
    "PRE_SIGNAL_SEMAPHORE": (0x04, 112, Action5BlockType.OFFSET),  # deprecated, use "SIGNALS" in all cases
    "PRE_SIGNAL_SEMAPHORE_PBS": (0x04, 240, Action5BlockType.OFFSET),  # deprecated, use "SIGNALS" in all cases
    "SIGNALS": (0x04, 240, Action5BlockType.OFFSET),
    "CATENARY": (0x05, 48, Action5BlockType.OFFSET),
    "FOUNDATIONS_SLOPES": (0x06, 74, Action5BlockType.FIXED),
    "FOUNDATIONS_SLOPES_HALFTILES": (0x06, 90, Action5BlockType.OFFSET),
    "TTDP_GUI_25": (0x07, 73, Action5BlockType.FIXED),
    "TTDP_GUI": (0x07, 93, Action5BlockType.FIXED),
    "CANALS": (0x08, 65, Action5BlockType.OFFSET),
    "ONE_WAY_ROAD": (0x09, 18, Action5BlockType.OFFSET),
    "COLOURMAP_2CC": (0x0A, 256, Action5BlockType.OFFSET),
    "TRAMWAY": (0x0B, 119, Action5BlockType.OFFSET),
    "SNOWY_TEMPERATE_TREES": (0x0C, 133, Action5BlockType.FIXED),
    "COAST_TILES": (0x0D, 16, Action5BlockType.FIXED),
    "COAST_TILES_BASEGFX": (0x0D, 10, Action5BlockType.FIXED),
    "COAST_TILES_DIAGONAL": (0x0D, 18, Action5BlockType.FIXED),
    "NEW_SIGNALS": (0x0E, 0, Action5BlockType.ANY),
    "SLOPED_RAILS": (0x0F, 12, Action5BlockType.OFFSET),
    "AIRPORTS": (0x10, 15, Action5BlockType.OFFSET),
    "ROAD_STOPS": (0x11, 8, Action5BlockType.OFFSET),
    "AQUEDUCTS": (0x12, 8, Action5BlockType.OFFSET),
    "AUTORAIL": (0x13, 55, Action5BlockType.OFFSET),
    "FLAGS": (0x14, 36, Action5BlockType.OFFSET),
    "OTTD_GUI": (0x15, 192, Action5BlockType.OFFSET),
    "AIRPORT_PREVIEW": (0x16, 9, Action5BlockType.OFFSET),
    "RAILTYPE_TUNNELS": (0x17, 16, Action5BlockType.OFFSET),
    "OTTD_RECOLOUR": (0x18, 1, Action5BlockType.OFFSET),
    "ROAD_WAYPOINTS": (0x19, 4, Action5BlockType.OFFSET),
    "OVERLAY_ROCKS": (0x1A, 95, Action5BlockType.OFFSET),
    "BRIDGE_DECKS": (0x1B, 24, Action5BlockType.OFFSET),
}


def parse_action5(replaces):
    real_sprite_list = real_sprite.parse_sprite_data(replaces)
    num_sprites = len(real_sprite_list)

    if replaces.type.value not in action5_table:
        raise generic.ScriptError(replaces.type.value + " is not a valid sprite replacement type", replaces.type.pos)
    type_id, num_required, block_type = action5_table[replaces.type.value]
    offset = None

    if block_type == Action5BlockType.FIXED:
        if num_sprites < num_required:
            msg = "Invalid sprite count for sprite replacement type '{}', expected {:d}, got {:d}"
            msg = msg.format(replaces.type, num_required, num_sprites)
            raise generic.ScriptError(msg, replaces.pos)

        elif num_sprites > num_required:
            msg = (
                "Too many sprites specified for sprite replacement type '{}',"
                " expected {:d}, got {:d}, extra sprites may be ignored"
            ).format(replaces.type, num_required, num_sprites)
            generic.print_warning(generic.Warning.GENERIC, msg, replaces.pos)

        if replaces.offset != 0:
            msg = "replacenew parameter 'offset' must be zero for sprite replacement type '{}'".format(replaces.type)
            raise generic.ScriptError(msg, replaces.pos)

    elif block_type == Action5BlockType.ANY:
        if replaces.offset != 0:
            msg = "replacenew parameter 'offset' must be zero for sprite replacement type '{}'".format(replaces.type)
            raise generic.ScriptError(msg, replaces.pos)

    elif block_type == Action5BlockType.OFFSET:
        if num_sprites + replaces.offset > num_required:
            msg = "Exceeding the limit of {:d} sprites for sprite replacement type '{}', extra sprites may be ignored"
            msg = msg.format(num_required, replaces.type)
            generic.print_warning(generic.Warning.GENERIC, msg, replaces.pos)

        if replaces.offset != 0 or num_sprites != num_required:
            offset = replaces.offset
    else:
        assert 0

    return [Action5(type_id, num_sprites, offset)] + real_sprite_list
