"""
Name:       r.info test
Purpose:    Tests r.info and its flags/options.

Author:     Sunveer Singh, Google Code-in 2017
Copyright:  (C) 2017 by Sunveer Singh and the GRASS Development Team
Licence:    This program is free software under the GNU General Public
                License (>=v2). Read the file COPYING that comes with GRASS
                for details.
"""

import json

from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import SimpleModule


class TestReport(TestCase):
    @classmethod
    def setUpClass(cls):
        """Use temporary region settings"""
        cls.use_temp_region()

    @classmethod
    def tearDownClass(cls):
        """!Remove the temporary region"""
        cls.del_temp_region()

    def test_flag_g(self):
        """Testing flag g with map lakes using simple module"""
        output_str = """north=228500
        south=215000
        east=645000
        west=630000
        nsres=10
        ewres=10
        rows=1350
        cols=1500
        cells=2025000
        datatype=CELL
        ncats=43600"""
        self.assertModuleKeyValue(
            module="r.info",
            map="lakes",
            flags="g",
            reference=output_str,
            precision=2,
            sep="=",
        )

        # Testing flag g and shell format with map lakes using simple module
        self.assertModuleKeyValue(
            module="r.info",
            map="lakes",
            flags="g",
            format="shell",
            reference=output_str,
            precision=2,
            sep="=",
        )

    def test_flag_r(self):
        """Testing flag r with map lakes using simple module"""
        output_str = """min=34300
        max=43600"""
        self.assertModuleKeyValue(
            module="r.info",
            map="lakes",
            flags="r",
            reference=output_str,
            precision=2,
            sep="=",
        )

        # Testing flag r and shell format with map lakes using simple module
        self.assertModuleKeyValue(
            module="r.info",
            map="lakes",
            flags="r",
            format="shell",
            reference=output_str,
            precision=2,
            sep="=",
        )

    def test_flag_s(self):
        """Testing flag s with map lakes using simple module"""
        output_str = """min=34300
        max=43600
        cells=2025000
        n=36011
        mean=39002.3492821638
        stddev=739.796536643115
        sum=1404513600"""
        self.assertModuleKeyValue(
            module="r.info",
            map="lakes",
            flags="s",
            reference=output_str,
            precision=2,
            sep="=",
        )

        # Testing flag s and shell format with map lakes using simple module
        self.assertModuleKeyValue(
            module="r.info",
            map="lakes",
            flags="s",
            format="shell",
            reference=output_str,
            precision=2,
            sep="=",
        )

    def test_flag_e(self):
        """Testing flag e with map lakes"""
        module = SimpleModule(
            "r.info",
            map="lakes",
            flags="e",
        )
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            "map=lakes",
            "maptype=raster",
            "mapset=PERMANENT",
            "location=",
            "project=",
            "database=",
            'date="Fri Jan 19 23:49:34 2007"',
            'creator="helena"',
            'title="South-West Wake county: Wake county lakes"',
            'timestamp="none"',
            'units="none"',
            'vdatum="none"',
            'semantic_label="none"',
            'source1=""',
            'source2=""',
            'description="generated by r.mapcalc"',
            'comments="1 * lakes_large"',
        ]

        # Skip exact match for lines containing "location", "project", "database" because their values vary
        for i, component in enumerate(result):
            if any(
                keyword in component for keyword in ("location", "project", "database")
            ):
                continue
            self.assertEqual(component, expected[i], f"Mismatch at line {i + 1}")

        # Testing flag e and shell format with map lakes using simple module
        shell_module = SimpleModule("r.info", map="lakes", flags="e", format="shell")
        self.assertModule(shell_module)
        shell_result = shell_module.outputs.stdout.splitlines()
        self.assertEqual(shell_result, result, "Mismatch in shell output (-e flag)")

    def test_flag_h(self):
        """Testing flag h with map lakes"""
        module = SimpleModule("r.info", map="lakes", flags="h")
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            "Data Source:",
            "   ",
            "   ",
            "Data Description:",
            "   generated by r.mapcalc",
            "Comments:",
            "   1 * lakes_large",
        ]

        self.assertListEqual(result, expected, "Mismatch in printed output (-h flag)")

        # Testing flag h and plain format with map lakes using simple module
        plain_module = SimpleModule("r.info", map="lakes", flags="h", format="plain")
        self.assertModule(plain_module)
        plain_result = plain_module.outputs.stdout.splitlines()
        self.assertEqual(plain_result, result, "Mismatch in plain output (-h flag)")

    def test_no_flag(self):
        """Testing without any flags and with map lakes"""
        module = SimpleModule("r.info", map="lakes")
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            " +----------------------------------------------------------------------------+",
            " | Map:      lakes                          Date: Fri Jan 19 23:49:34 2007    |",
            " | Mapset:   PERMANENT                      Login of Creator: helena          |",
            " | Project:                                                                   |",
            " | DataBase:                                                                  |",
            " | Title:    South-West Wake county: Wake county lakes                        |",
            " | Timestamp: none                                                            |",
            " |----------------------------------------------------------------------------|",
            " |                                                                            |",
            " |   Type of Map:  raster               Number of Categories: 43600           |",
            " |   Data Type:    CELL                 Semantic label: (none)                |",
            " |   Rows:         1350                                                       |",
            " |   Columns:      1500                                                       |",
            " |   Total Cells:  2025000                                                    |",
            " |        Projection: Lambert Conformal Conic                                 |",
            " |            N:     228500    S:     215000   Res:    10                     |",
            " |            E:     645000    W:     630000   Res:    10                     |",
            " |   Range of data:    min = 34300  max = 43600                               |",
            " |                                                                            |",
            " |   Data Description:                                                        |",
            " |    generated by r.mapcalc                                                  |",
            " |                                                                            |",
            " |   Comments:                                                                |",
            " |    1 * lakes_large                                                         |",
            " |                                                                            |",
            " +----------------------------------------------------------------------------+",
            "",
        ]

        # Skip exact match for lines containing "Project:" or "DataBase:" because their values vary
        for i, component in enumerate(result):
            if "Project:" in component or "DataBase:" in component:
                continue
            self.assertEqual(component, expected[i], f"Mismatch at line {i + 1}")

        # Testing without any flags and plain format with map lakes using simple module
        plain_module = SimpleModule("r.info", map="lakes", format="plain")
        self.assertModule(plain_module)
        plain_result = plain_module.outputs.stdout.splitlines()
        self.assertEqual(plain_result, result, "Mismatch in plain output (no flags)")

    def test_flags_grse(self):
        """Testing flags grse with map lakes"""
        module = SimpleModule(
            "r.info",
            map="lakes",
            flags="grse",
        )
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            "north=228500",
            "south=215000",
            "east=645000",
            "west=630000",
            "nsres=10",
            "ewres=10",
            "rows=1350",
            "cols=1500",
            "cells=2025000",
            "datatype=CELL",
            "ncats=43600",
            "min=34300",
            "max=43600",
            "n=36011",
            "mean=39002.3492821638",
            "stddev=739.796536643115",
            "sum=1404513600",
            "map=lakes",
            "maptype=raster",
            "mapset=PERMANENT",
            "location=",
            "project=",
            "database=",
            'date="Fri Jan 19 23:49:34 2007"',
            'creator="helena"',
            'title="South-West Wake county: Wake county lakes"',
            'timestamp="none"',
            'units="none"',
            'vdatum="none"',
            'semantic_label="none"',
            'source1=""',
            'source2=""',
            'description="generated by r.mapcalc"',
            'comments="1 * lakes_large"',
        ]

        # Skip exact match for lines containing "location", "project", "database" because their values vary
        for i, component in enumerate(result):
            if any(
                keyword in component for keyword in ("location", "project", "database")
            ):
                continue
            self.assertEqual(component, expected[i], f"Mismatch at line {i + 1}")

        # Testing flag grse and shell format with map lakes using simple module
        shell_module = SimpleModule("r.info", map="lakes", flags="grse", format="shell")
        self.assertModule(shell_module)
        shell_result = shell_module.outputs.stdout.splitlines()
        self.assertEqual(shell_result, result, "Mismatch in shell output (-grse flags)")

    def test_no_flags_shell(self):
        """Testing without any flags with shell format with map geology_30m"""
        module = SimpleModule("r.info", map="geology_30m", format="shell")
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            "north=228500",
            "south=215000",
            "east=645000",
            "west=630000",
            "nsres=30",
            "ewres=30",
            "rows=450",
            "cols=500",
            "cells=225000",
            "datatype=CELL",
            "ncats=1832",
            "min=217",
            "max=948",
            "n=225000",
            "mean=311.219635555556",
            "stddev=165.83590845639",
            "sum=70024418",
            "map=geology_30m",
            "maptype=raster",
            "mapset=PERMANENT",
            "location=",
            "project=",
            "database=",
            'date="Fri Mar 16 15:49:04 2007"',
            'creator="hmitaso"',
            'title="South-West Wake county: geology derived from vector map"',
            'timestamp="none"',
            'units="none"',
            'vdatum="none"',
            'semantic_label="none"',
            'source1="Vector Map: geology in mapset PERMANENT"',
            'source2="Original Scale from Vector Map: 1:1"',
            'description="generated by v.to.rast"',
            'comments="v.to.rast input="geology" output="geology_30m" use="attr" type="poin\\t,line,area" layer=1 column="GEOL250_ID" value=1 rows=4096 labelcolu\\mn="GEO_NAME""',
        ]

        # Skip exact match for lines containing "location", "project", "database" because their values vary
        for i, component in enumerate(result):
            if any(
                keyword in component for keyword in ("location", "project", "database")
            ):
                continue
            self.assertEqual(component, expected[i], f"Mismatch at line {i + 1}")

    def test_flag_h_shell(self):
        """Testing flag h and shell format with map lakes"""
        output_str = """source1=""
        source2=""
        description="generated by r.mapcalc"
        comments="1 * lakes_large\""""
        self.assertModuleKeyValue(
            module="r.info",
            map="lakes",
            flags="h",
            format="shell",
            reference=output_str,
            precision=2,
            sep="=",
        )

    def test_flag_g_plain(self):
        """Testing flag g and plain format with map lsat7_2002_50"""
        module = SimpleModule("r.info", map="lsat7_2002_50", flags="g", format="plain")
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            "North: 228513",
            "South: 214975.5",
            "East: 645012",
            "West: 629992.5",
            "North-south resolution: 28.5",
            "East-west resolution: 28.5",
            "Rows: 475",
            "Columns: 527",
            "Total cells: 250325",
            "Data type: CELL",
            "Number of categories: 255",
        ]

        self.assertEqual(result, expected, "Mismatch in plain output (-g flag)")

    def test_flag_r_plain(self):
        """Testing flag r and plain format with map lsat7_2002_50"""
        module = SimpleModule("r.info", map="lsat7_2002_50", flags="r", format="plain")
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = ["Minimum: 1", "Maximum: 255"]

        self.assertEqual(result, expected, "Mismatch in plain output (-r flag)")

    def test_flag_s_plain(self):
        """Testing flag s and plain format with map lsat7_2002_50"""
        module = SimpleModule("r.info", map="lsat7_2002_50", flags="s", format="plain")
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            "Minimum: 1",
            "Maximum: 255",
            "Total cells: 250325",
            "N: 250325",
            "Mean: 97.5980225706581",
            "Standard deviation: 37.4386469322189",
            "Sum: 24431225",
        ]

        self.assertEqual(result, expected, "Mismatch in plain output (-s flag)")

    def test_flag_e_plain(self):
        """Testing flag e and plain format with map lakes"""
        module = SimpleModule("r.info", map="lakes", flags="e", format="plain")
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            "Map: lakes",
            "Maptype: raster",
            "Mapset: PERMANENT",
            "Location: ",
            "Project: ",
            "Database: ",
            "Date: Fri Jan 19 23:49:34 2007",
            "Creator: helena",
            "Title: South-West Wake county: Wake county lakes",
            "Timestamp: none",
            "Data units: none",
            "Vertical datum: none",
            "Semantic label: none",
            "Data source:",
            "   ",
            "   ",
            "Data description:",
            "   generated by r.mapcalc",
            "Comments:",
            "   1 * lakes_large",
        ]

        # Skip exact match for lines containing "Location: ", "Project: ", "Database: " because their values vary
        for i, component in enumerate(result):
            if any(
                keyword in component
                for keyword in ("Location: ", "Project: ", "Database: ")
            ):
                continue
            self.assertEqual(component, expected[i], f"Mismatch at line {i + 1}")

    def test_flags_grse_plain(self):
        """Testing flags grse and plain format with map lakes"""
        module = SimpleModule("r.info", map="lakes", flags="grse", format="plain")
        self.assertModule(module)
        result = module.outputs.stdout.splitlines()
        expected = [
            "North: 228500",
            "South: 215000",
            "East: 645000",
            "West: 630000",
            "North-south resolution: 10",
            "East-west resolution: 10",
            "Rows: 1350",
            "Columns: 1500",
            "Total cells: 2025000",
            "Data type: CELL",
            "Number of categories: 43600",
            "Minimum: 34300",
            "Maximum: 43600",
            "N: 36011",
            "Mean: 39002.3492821638",
            "Standard deviation: 739.796536643115",
            "Sum: 1404513600",
            "Map: lakes",
            "Maptype: raster",
            "Mapset: PERMANENT",
            "Location: ",
            "Project: ",
            "Database: ",
            "Date: Fri Jan 19 23:49:34 2007",
            "Creator: helena",
            "Title: South-West Wake county: Wake county lakes",
            "Timestamp: none",
            "Data units: none",
            "Vertical datum: none",
            "Semantic label: none",
            "Data source:",
            "   ",
            "   ",
            "Data description:",
            "   generated by r.mapcalc",
            "Comments:",
            "   1 * lakes_large",
        ]

        # Skip exact match for lines containing "Location: ", "Project: ", "Database: " because their values vary
        for i, component in enumerate(result):
            if any(
                keyword in component
                for keyword in ("Location: ", "Project: ", "Database: ")
            ):
                continue
            self.assertEqual(component, expected[i], f"Mismatch at line {i + 1}")

    def _test_format_json_helper(self, module, expected, check_variable_fields=True):
        self.runModule(module)
        result = json.loads(module.outputs.stdout)

        # If requested, verify that the fields which vary with the Grass sample
        # data's path exist in the JSON output and not exact values
        if check_variable_fields:
            remove_fields = ["location", "project", "database"]
            for field in remove_fields:
                self.assertIn(field, result)
                result.pop(field)

        self.assertCountEqual(list(expected.keys()), list(result.keys()))

        for key, value in expected.items():
            if isinstance(value, float):
                self.assertAlmostEqual(value, result[key])
            else:
                self.assertEqual(value, result[key])

    def test_flag_g_json(self):
        """Testing flag g and JSON format with map zipcodes using simple module"""
        module = SimpleModule("r.info", map="zipcodes", flags="g", format="json")
        expected = {
            "north": 228500,
            "south": 215000,
            "nsres": 10,
            "east": 645000,
            "west": 630000,
            "ewres": 10,
            "rows": 1350,
            "cols": 1500,
            "cells": 2025000,
            "datatype": "CELL",
            "ncats": 27610,
        }
        self._test_format_json_helper(module, expected, False)

    def test_flag_r_json(self):
        """Testing flag r and JSON format with map landcover_1m using simple module"""
        module = SimpleModule("r.info", map="landcover_1m", flags="r", format="json")
        expected = {"min": 1, "max": 11}
        self._test_format_json_helper(module, expected, False)

    def test_flag_s_json(self):
        """Testing flag s and JSON format with map lakes using simple module"""
        module = SimpleModule("r.info", map="lakes", flags="s", format="json")
        expected = {
            "min": 34300,
            "max": 43600,
            "cells": 2025000,
            "n": 36011,
            "mean": 39002.349282163785,
            "stddev": 739.79653664311479,
            "sum": 1404513600,
        }
        self._test_format_json_helper(module, expected, False)

    def test_flag_e_json(self):
        """Testing flag e and JSON format with map landcover_1m"""
        module = SimpleModule("r.info", map="landcover_1m", flags="e", format="json")
        expected = {
            "map": "landcover_1m",
            "maptype": "raster",
            "mapset": "PERMANENT",
            "date": "Fri Mar  9 20:01:46 2007",
            "creator": "helena",
            "title": "Rural area: Landcover",
            "timestamp": None,
            "units": None,
            "vdatum": None,
            "semantic_label": None,
            "source1": "",
            "source2": "",
            "description": "generated by r.mapcalc",
            "comments": "if(isnull(landcover_1m) || landcover_1m == 0, 11, landcover_1m)",
        }
        self._test_format_json_helper(module, expected)

    def test_flag_h_json(self):
        """Testing flag h and JSON format with map zipcodes"""
        module = SimpleModule("r.info", map="zipcodes", flags="h", format="json")
        expected = {
            "source1": "",
            "source2": "",
            "description": "generated by r.mapcalc",
            "comments": "int(zipcodes)",
        }
        self._test_format_json_helper(module, expected, False)

    def test_no_flag_json(self):
        """Testing without any flags with JSON format and with map lakes"""
        module = SimpleModule("r.info", map="lakes", format="json")
        expected = {
            "north": 228500,
            "south": 215000,
            "nsres": 10,
            "east": 645000,
            "west": 630000,
            "ewres": 10,
            "rows": 1350,
            "cols": 1500,
            "cells": 2025000,
            "datatype": "CELL",
            "ncats": 43600,
            "min": 34300,
            "max": 43600,
            "n": 36011,
            "mean": 39002.349282163785,
            "stddev": 739.79653664311479,
            "sum": 1404513600,
            "map": "lakes",
            "maptype": "raster",
            "mapset": "PERMANENT",
            "date": "Fri Jan 19 23:49:34 2007",
            "creator": "helena",
            "title": "South-West Wake county: Wake county lakes",
            "timestamp": None,
            "units": None,
            "vdatum": None,
            "semantic_label": None,
            "source1": "",
            "source2": "",
            "description": "generated by r.mapcalc",
            "comments": "1 * lakes_large",
        }
        self._test_format_json_helper(module, expected)

    def test_flags_grse_json(self):
        """Testing flags grse and JSON format with map lakes"""
        module = SimpleModule("r.info", map="lakes", flags="grse", format="json")
        expected = {
            "north": 228500,
            "south": 215000,
            "nsres": 10,
            "east": 645000,
            "west": 630000,
            "ewres": 10,
            "rows": 1350,
            "cols": 1500,
            "cells": 2025000,
            "datatype": "CELL",
            "ncats": 43600,
            "min": 34300,
            "max": 43600,
            "n": 36011,
            "mean": 39002.349282163785,
            "stddev": 739.79653664311479,
            "sum": 1404513600,
            "map": "lakes",
            "maptype": "raster",
            "mapset": "PERMANENT",
            "date": "Fri Jan 19 23:49:34 2007",
            "creator": "helena",
            "title": "South-West Wake county: Wake county lakes",
            "timestamp": None,
            "units": None,
            "vdatum": None,
            "semantic_label": None,
            "source1": "",
            "source2": "",
            "description": "generated by r.mapcalc",
            "comments": "1 * lakes_large",
        }
        self._test_format_json_helper(module, expected)


class TestComments(TestCase):
    """Check printing of comments"""

    def test_comments_one_line(self):
        """Check that one line is text without any newlines"""
        module = SimpleModule("r.info", map="lakes", format="json")
        self.runModule(module)
        result = json.loads(module.outputs.stdout)
        self.assertFalse(result["comments"].endswith("\n"))
        self.assertEqual(result["comments"], "1 * lakes_large")

    def test_comments_continued_line(self):
        """Check that continued lines are merged"""
        module = SimpleModule("r.info", map="elevation", format="json")
        self.runModule(module)
        result = json.loads(module.outputs.stdout)
        self.assertFalse(result["comments"].endswith("\n"))
        self.assertEqual(
            result["comments"],
            'r.proj input="ned03arcsec" location="northcarolina_latlong" '
            'mapset="helena" output="elev_ned10m" method="cubic" resolution=10',
        )

    def test_comments_multiple_lines(self):
        """Check multiple lines are preserved"""
        module = SimpleModule("r.info", map="lsat7_2002_30", format="json")
        self.runModule(module)
        result = json.loads(module.outputs.stdout)
        self.assertFalse(result["comments"].endswith("\n"))

        lines = result["comments"].splitlines()
        self.assertEqual(
            len(lines),
            31,
        )
        self.assertEqual(
            lines[0],
            'r.in.gdal input="p016r035_7t20020524_z17_nn30_nc_spm_wake.tif" output="lsat7_2002_30"',
        )
        self.assertEqual(
            lines[-1],
            'i.landsat.rgb "b=lsat7_2002_10" "g=lsat7_2002_20" "r=lsat7_2002_30"',
        )


if __name__ == "__main__":
    from grass.gunittest.main import test

    test()
