# Contributed by
# Dany Zatuchna (danzat at gmail)
""" Implementation of the following grammar for the GIF89a file format
<GIF Data Stream> ::=     Header <Logical Screen> <Data>* Trailer

<Logical Screen> ::=      Logical Screen Descriptor [Global Color Table]

<Data> ::=                <Graphic Block>  |
                          <Special-Purpose Block>

<Graphic Block> ::=       [Graphic Control Extension] <Graphic-Rendering Block>

<Graphic-Rendering Block> ::=  <Table-Based Image>  |
                               Plain Text Extension

<Table-Based Image> ::=   Image Descriptor [Local Color Table] Image Data

<Special-Purpose Block> ::=    Application Extension  |
                               Comment Extension
"""
from construct import *
from construct.lib.py3compat import b

data_sub_block = Struct("data_sub_block",
                        ULInt8("size"),
                        String("data", lambda ctx: ctx["size"])
                        )

gif_logical_screen = Struct("logical_screen",
                            ULInt16("width"),
                            ULInt16("height"),
                            BitStruct("flags",
                                      Bit("global_color_table"),
                                      BitField("color_resolution", 3),
                                      Bit("sort_flag"),
                                      BitField("global_color_table_bpp", 3)
                                      ),
                            ULInt8("bgcolor_index"),
                            ULInt8("pixel_aspect_ratio"),
                            If(lambda ctx: ctx["flags"]["global_color_table"],
                               Array(lambda ctx: 2 ** (ctx["flags"]["global_color_table_bpp"] + 1),
                                     Struct("palette",
                                            ULInt8("R"),
                                            ULInt8("G"),
                                            ULInt8("B")
                                            )
                                     )
                               )
                            )

gif_header = Struct("gif_header",
                    Const(String("signature", 3), b("GIF")),
                    Const(String("version", 3), b("89a")),
                    )

application_extension = Struct("application_extension",
                               Const(ULInt8("block_size"), 11),
                               String("application_identifier", 8),
                               String("application_auth_code", 3),
                               data_sub_block,
                               ULInt8("block_terminator")
                               )

comment_extension = Struct("comment_extension",
                           data_sub_block,
                           ULInt8("block_terminator")
                           )

graphic_control_extension = Struct("graphic_control_extension",
                                   Const(ULInt8("block_size"), 4),
                                   BitStruct("flags",
                                             BitField("reserved", 3),
                                             BitField("disposal_method", 3),
                                             Bit("user_input_flag"),
                                             Bit("transparent_color_flag"),
                                             ),
                                   ULInt16("delay"),
                                   ULInt8("transparent_color_index"),
                                   ULInt8("block_terminator")
                                   )

plain_text_extension = Struct("plain_text_extension",
                              Const(ULInt8("block_size"), 12),
                              ULInt16("text_left"),
                              ULInt16("text_top"),
                              ULInt16("text_width"),
                              ULInt16("text_height"),
                              ULInt8("cell_width"),
                              ULInt8("cell_height"),
                              ULInt8("foreground_index"),
                              ULInt8("background_index"),
                              data_sub_block,
                              ULInt8("block_terminator")
                              )

extension = Struct("extension",
                   ULInt8("label"),
                   Switch("ext", lambda ctx: ctx["label"], {
                       0xFF: application_extension,
                       0xFE: comment_extension,
                       0xF9: graphic_control_extension,
                       0x01: plain_text_extension
                   })
                   )

image_descriptor = Struct("image_descriptor",
                          ULInt16("left"),
                          ULInt16("top"),
                          ULInt16("width"),
                          ULInt16("height"),
                          BitStruct("flags",
                                    Bit("local_color_table"),
                                    Bit("interlace"),
                                    Bit("sort"),
                                    BitField("reserved", 2),
                                    BitField("local_color_table_bpp", 3)
                                    ),
                          If(lambda ctx: ctx["flags"]["local_color_table"],
                             Array(lambda ctx: 2 ** (ctx["flags"]["local_color_table_bpp"] + 1),
                                   Struct("palette",
                                          ULInt8("R"),
                                          ULInt8("G"),
                                          ULInt8("B")
                                          )
                                   )
                             ),
                          ULInt8("LZW_minimum_code_size"),
                          RepeatUntil(lambda obj, ctx: obj.size == 0, data_sub_block)
                          )

gif_data = Struct("gif_data",
                  ULInt8("introducer"),
                  Switch("dat", lambda ctx: ctx["introducer"], {
                      0x21: extension,
                      0x2C: image_descriptor
                  })
                  )

gif_file = Struct("gif_file",
                  gif_header,
                  gif_logical_screen,
                  OptionalGreedyRange(gif_data),
                  # Const(ULInt8("trailer"), 0x3B)
                  )

if __name__ == "__main__":
    f = open("../../../tests/sample.gif", "rb")
    s = f.read()
    f.close()
    print(gif_file.parse(s))
