# encoding=utf-8
# Copyright © 2016 Intel Corporation

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""Generates isl_format_layout.c."""

from __future__ import absolute_import, division, print_function
import argparse
import csv
import re
import textwrap

from mako import template

# Load the template, ensure that __future__.division is imported, and set the
# bytes encoding to be utf-8. This last bit is important to getting simple
# consistent behavior for python 3 when we get there.
TEMPLATE = template.Template(
    text=textwrap.dedent("""\
        /* This file is autogenerated by gen_format_layout.py. DO NOT EDIT! */

        /*
         * Copyright 2015 Intel Corporation
         *
         *  Permission is hereby granted, free of charge, to any person obtaining a
         *  copy of this software and associated documentation files (the "Software"),
         *  to deal in the Software without restriction, including without limitation
         *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
         *  and/or sell copies of the Software, and to permit persons to whom the
         *  Software is furnished to do so, subject to the following conditions:
         *
         *  The above copyright notice and this permission notice (including the next
         *  paragraph) shall be included in all copies or substantial portions of the
         *  Software.
         *
         *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
         *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
         *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
         *  IN THE SOFTWARE.
         */

        #include "isl/isl.h"

        const struct isl_format_layout
        isl_format_layouts[] = {
        % for format in formats:
          [ISL_FORMAT_${format.name}] = {
            .format = ISL_FORMAT_${format.name},
            .name = "ISL_FORMAT_${format.name}",
            .bpb = ${format.bpb},
            .bw = ${format.bw},
            .bh = ${format.bh},
            .bd = ${format.bd},
            .channels = {
            % for mask in ['r', 'g', 'b', 'a', 'l', 'i', 'p']:
              <% channel = getattr(format, mask, None) %>\\
              % if channel.type is not None:
                .${mask} = { ISL_${channel.type}, ${channel.size} },
              % else:
                .${mask} = {},
              % endif
            % endfor
            },
            .colorspace = ISL_COLORSPACE_${format.colorspace},
            .txc = ISL_TXC_${format.txc},
          },

        % endfor
        };
    """),
    future_imports=['division'],
    output_encoding='utf-8')


class Channel(object):
    """Class representing a Channel.

    Converts the csv encoded data into the format that the template (and thus
    the consuming C code) expects.

    """
    # If the csv file grew very large this class could be put behind a factory
    # to increase efficiency. Right now though it's fast enough that It didn't
    # seem worthwhile to add all of the boilerplate
    _types = {
        'x': 'void',
        'r': 'raw',
        'un': 'unorm',
        'sn': 'snorm',
        'uf': 'ufloat',
        'sf': 'sfloat',
        'ux': 'ufixed',
        'sx': 'sfixed',
        'ui': 'uint',
        'si': 'sint',
        'us': 'uscaled',
        'ss': 'sscaled',
    }
    _splitter = re.compile(r'\s*(?P<type>[a-z]+)(?P<size>[0-9]+)')

    def __init__(self, line):
        # If the line is just whitespace then just set everything to None to
        # save on the regex cost and let the template skip on None.
        if line.isspace():
            self.size = None
            self.type = None
        else:
            grouped = self._splitter.match(line)
            self.type = self._types[grouped.group('type')].upper()
            self.size = grouped.group('size')


class Format(object):
    """Class taht contains all values needed by the template."""
    def __init__(self, line):
        # pylint: disable=invalid-name
        self.name = line[0].strip()

        # Future division makes this work in python 2.
        self.bpb = int(line[1])
        self.bw = line[2].strip()
        self.bh = line[3].strip()
        self.bd = line[4].strip()
        self.r = Channel(line[5])
        self.g = Channel(line[6])
        self.b = Channel(line[7])
        self.a = Channel(line[8])
        self.l = Channel(line[9])
        self.i = Channel(line[10])
        self.p = Channel(line[11])

        # alpha doesn't have a colorspace of it's own.
        self.colorspace = line[12].strip().upper()
        if self.colorspace in ['', 'ALPHA']:
            self.colorspace = 'NONE'

        # This sets it to the line value, or if it's an empty string 'NONE'
        self.txc = line[13].strip().upper() or 'NONE'


def reader(csvfile):
    """Wrapper around csv.reader that skips comments and blanks."""
    # csv.reader actually reads the file one line at a time (it was designed to
    # open excel generated sheets), so hold the file until all of the lines are
    # read.
    with open(csvfile, 'r') as f:
        for line in csv.reader(f):
            if line and not line[0].startswith('#'):
                yield line


def main():
    """Main function."""
    parser = argparse.ArgumentParser()
    parser.add_argument('--csv', action='store', help='The CSV file to parse.')
    parser.add_argument(
        '--out',
        action='store',
        help='The location to put the generated C file.')
    args = parser.parse_args()

    # This generator opens and writes the file itself, and it does so in bytes
    # mode. This solves both python 2 vs 3 problems and solves the locale
    # problem: Unicode can be rendered even if the shell calling this script
    # doesn't.
    with open(args.out, 'wb') as f:
        try:
            # This basically does lazy evaluation and initialization, which
            # saves on memory and startup overhead.
            f.write(TEMPLATE.render(
                formats=(Format(l) for l in reader(args.csv))))
        except Exception:
            # In the even there's an error this imports some helpers from mako
            # to print a useful stack trace and prints it, then exits with
            # status 1, if python is run with debug; otherwise it just raises
            # the exception
            if __debug__:
                import sys
                from mako import exceptions
                print(exceptions.text_error_template().render(),
                      file=sys.stderr)
                sys.exit(1)
            raise


if __name__ == '__main__':
    main()
