# Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# This software is available to you under a choice of one of two
# licenses.  You may choose to be licensed under the terms of the GNU
# General Public License (GPL) Version 2, available from the file
# COPYING in the main directory of this source tree, or the
# OpenIB.org BSD license below:
#
#     Redistribution and use in source and binary forms, with or
#     without modification, are permitted provided that the following
#     conditions are met:
#
#      - Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      - Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials
#        provided with the distribution.
#
# 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.

#######################################################
#
# SegmentCreator.py
# Python implementation of the Class SegmentCreator
# Generated by Enterprise Architect
# Created on:      14-Aug-2019 10:12:03 AM
# Original author: talve
#
#######################################################
from segments.SegmentFactory import SegmentFactory
from segments.ResourceSegment import ResourceSegment
from segments.Segment import unpack_segment_header
from resourcedump_lib.utils import constants as cs
from resourceparse_lib.utils.common_functions import is_resource_segment


class SegmentCreator:
    """this class is responsible for splitting  the raw data to segments and creating
    segments objects.
    """

    @classmethod
    def create(cls, raw_data, aggregate=False):
        """convert segments data into a segments objects by using SegmentFactory.
        """
        try:
            segments = []
            starting_resource_type = None

            current_index = 0
            end_index = len(raw_data)
            while current_index < end_index:
                seg_size_dword, seg_id = unpack_segment_header(raw_data, current_index)

                if seg_size_dword == 0:
                    raise Exception("raw_data didn't get smaller - found segment_size = 0")

                seg_size = seg_size_dword * cs.DWORD_SIZE
                raw_seg = raw_data[current_index:current_index + seg_size]
                seg_type = cls.get_seg_type_for_register_segments(seg_id)
                seg = SegmentFactory.create(seg_type, raw_seg)
                seg.resource_type = seg_id

                is_resource_segment = isinstance(seg, ResourceSegment)
                to_aggregate = aggregate and is_resource_segment and seg.unpack_aggregate_bit()

                if not to_aggregate:
                    if is_resource_segment:
                        starting_resource_type = seg_id
                    segments.append(seg)
                else:
                    if not starting_resource_type or seg_id != starting_resource_type:
                        raise Exception("Aggregate bit on first or control segment is illegal")
                    else:
                        segments[-1].aggregate(seg)

                current_index += seg_size

        except Exception as e:
            raise Exception("Failed to create segments with error: {0}".format(e))
        return segments

    @classmethod
    def get_seg_type_for_register_segments(cls, seg_type):
        """This method check if the segment type is a reference segment
           and return the right type of that segment.
        """
        if is_resource_segment(seg_type):
            return cs.RESOURCE_DUMP_SEGMENT_TYPE_RESOURCE
        return seg_type
