/*
Copyright 2011-2016 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.google.security.zynamics.binnavi.disassembly.types;

import com.google.common.base.Preconditions;
import com.google.security.zynamics.binnavi.Gui.GraphWindows.CommentDialogs.Interfaces.IComment;
import com.google.security.zynamics.binnavi.disassembly.CommentManager;
import com.google.security.zynamics.binnavi.disassembly.INaviModule;
import com.google.security.zynamics.zylib.disassembly.IAddress;

import java.math.BigInteger;
import java.util.List;

public final class Section {
  /**
   * The id of the {@link Section}.
   */
  private final int id;

  /**
   * The name of the {@link Section}.
   */
  private final String name;

  /**
   * The start {@link IAddress} of the {@link Section}.
   */
  private final IAddress startAddress;

  /**
   * The end {@link IAddress} of the {@link Section}.
   */
  private final IAddress endAddress;

  /**
   * The {@link SectionPermission} of the {@link Section}.
   */
  private final SectionPermission permission;

  /**
   * The stored data of the {@link Section} can be empty.
   */
  private final byte[] data;

  /**
   * The virtual size of the {@link Section}
   */
  private final long virtualSize;

  /**
   * The {@link CommentManager} used for {@link IComment} manipulation associated with the
   * {@link Section}.
   */
  private final CommentManager commentManager;

  /**
   * The {@link INaviModule} of the {@link Section}.
   */
  private final INaviModule module;

  /**
   * Creates a new {@link Section}.
   *
   * @param id The id of the {@link Section} generated by the database.
   * @param name The name of the {@link Section}.
   * @param commentManager The {@link CommentManager} who is in charge of the comments associated to
   *        this {@link Section}.
   * @param module The {@link INaviModule} the {@link Section} is associated to.
   * @param startAddress The start {@link IAddress} of the {@link Section}.
   * @param endAddress The end {@link IAddress} of the {@link Section}.
   * @param permission The {@link SectionPermission} of the {@link Section}.
   * @param data The data of the {@link Section}.
   */
  public Section(final int id, final String name, final CommentManager commentManager,
      final INaviModule module, final IAddress startAddress, final IAddress endAddress,
      final SectionPermission permission, final byte[] data) {
    Preconditions.checkArgument(id >= 0, "Error: id argument must be larger or equal to zero");
    this.id = id;
    this.name = Preconditions.checkNotNull(name, "Error: name argument can not be null");
    this.commentManager = Preconditions.checkNotNull(
        commentManager, "Error: commentManager argument can not be null");
    this.module = Preconditions.checkNotNull(module, "Error: module argument can not be null");
    Preconditions.checkArgument(
        startAddress.compareTo(endAddress) <= 0,
        "Error: end address must be larger or equal to start address");
    this.startAddress =
        Preconditions.checkNotNull(startAddress, "Error: startAddress argument can not be null");
    this.endAddress =
        Preconditions.checkNotNull(endAddress, "Error: endAddress argument can not be null");
    this.permission =
        Preconditions.checkNotNull(permission, "Error: permission argument can not be null");
    this.data = data;
    virtualSize = (endAddress.toBigInteger().subtract(startAddress.toBigInteger())).longValue();
  }

  /**
   * Getter for the {@link Section} {@link IComment}.
   *
   * @return {@link IComment} of the {@link Section}.
   */
  public List<IComment> getComments() {
    return commentManager.getSectionComments(this);
  }

  /**
   * Return the {@link Section} data.
   *
   * @return the {@link Section} data.
   */
  public byte[] getData() {
    return data;
  }

  /**
   * Return the end {@link IAddress} of the {@link Section}.
   *
   * @return The end {@link IAddress} of the {@link Section}.
   */
  public IAddress getEndAddress() {
    return endAddress;
  }

  /**
   * Return the id of the {@link Section}.
   *
   * @return The id of the {@link Section}.
   */
  public int getId() {
    return id;
  }

  /**
   * Returns the {@link INaviModule} the {@link Section} is associated to.
   *
   * @return the {@link INaviModule} the {@link Section} is associated to.
   */
  public INaviModule getModule() {
    return module;
  }

  /**
   * Return the name of the {@link Section}.
   *
   * @return the name of the {@link Section}.
   */

  public String getName() {
    return name;
  }

  /**
   * Returns the static size of the section data as stored in the corresponding module file in
   * number of bytes.
   *
   * @return The static size of the section data in number of bytes.
   */
  public int getRawSize() {
    return data != null ? data.length : 0;
  }

  /**
   * Returns the {@link SectionPermission} of the {@link Section}.
   *
   * @return the {@link SectionPermission} of the {@link Section}.
   */
  public SectionPermission getSectionPermission() {
    return permission;
  }

  /**
   * Returns the start {@link IAddress} of the {@link Section}.
   *
   * @return the start {@link IAddress} of the {@link Section}.
   */
  public IAddress getStartAddress() {
    return startAddress;
  }

  /**
   * Returns the size the section has at runtime when the corresponding module is mapped to the
   * address space.
   *
   * @return The size of the section at runtime.
   */
  public long getVirtualSize() {
    return virtualSize;
  }

  /**
   * Returns true iff the given offset is within the range 0, getVirtualSize()).
   *
   * @param offset The offset to check.
   * @return True iff the given offset is within the range [0, getVirtualSize()).
   */
  public boolean isValidOffset(final long offset) {
    if (offset < 0) {
      return false;
    }
    return BigInteger.valueOf(offset).compareTo(BigInteger.valueOf(virtualSize)) < 0;
  }

  public boolean isValidAddress(final Long address) {
    Preconditions.checkNotNull(address, "Error: address argument can not be null");
    if (address < startAddress.toLong() || address > endAddress.toLong()) {
      return false;
    }
    return true;
  }
}
