class Partition < ApplicationRecord
  belongs_to :disk
  belongs_to :hardware
  has_many :volumes,
           lambda { |_|
             p = Partition.quoted_table_name
             v = Volume.quoted_table_name
             Volume.select("DISTINCT #{v}.*")
               .joins("JOIN #{p} ON #{v}.hardware_id = #{p}.hardware_id AND #{v}.volume_group = #{p}.volume_group")
               .where("#{p}.id" => id)
           }, :foreign_key => :volume_group

  virtual_column :aligned, :type => :boolean

  def volume_group
    # Override volume_group getter to prevent the special physical linkage from coming through
    vg = read_attribute(:volume_group)
    return nil if vg.respond_to?(:starts_with?) && vg.starts_with?(Volume::PHYSICAL_VOLUME_GROUP)

    vg
  end

  # Derived from linux fdisk "list known partition types"
  # More info at http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
  PARTITION_TYPE_NAMES = {
    0x00 => "Empty",
    0x01 => "FAT12",
    0x02 => "XENIX root",
    0x03 => "XENIX usr",
    0x04 => "FAT16 <32M",
    0x05 => "Extended",
    0x06 => "FAT16",
    0x07 => "HPFS/NTFS",
    0x08 => "AIX",
    0x09 => "AIX bootable",
    0x0a => "OS/2 Boot Manager",
    0x0b => "W95 FAT32",
    0x0c => "W95 FAT32 (LBA)",
    0x0e => "W95 FAT16 (LBA)",
    0x0f => "W95 Ext'd (LBA)",
    0x10 => "OPUS",
    0x11 => "Hidden FAT12",
    0x12 => "Compaq diagnostic",
    0x14 => "Hidden FAT16 <32M",
    0x16 => "Hidden FAT16",
    0x17 => "Hidden HPFS/NTFS",
    0x18 => "AST SmartSleep",
    0x1b => "Hidden W95 FAT32",
    0x1c => "Hidden W95 FAT32 (LBA)",
    0x1e => "Hidden W95 FAT16 (LBA)",
    0x24 => "NEC DOS",
    0x39 => "Plan 9",
    0x3c => "PartitionMagic",
    0x40 => "Venix 80286",
    0x41 => "PPC PReP Boot",
    0x42 => "SFS",
    0x4d => "QNX4.x",
    0x4e => "QNX4.x 2nd part",
    0x4f => "QNX4.x 3rd part",
    0x50 => "OnTrack DM",
    0x51 => "OnTrack DM6 Aux1",
    0x52 => "CP/M",
    0x53 => "OnTrack DM6 Aux3",
    0x54 => "OnTrackDM6",
    0x55 => "EZ-Drive",
    0x56 => "Golden Bow",
    0x5c => "Priam Edisk",
    0x61 => "SpeedStor",
    0x63 => "GNU HURD or System V",
    0x64 => "Novell Netware 286",
    0x65 => "Novell Netware 386",
    0x70 => "DiskSecure MultiBoot",
    0x75 => "PC/IX",
    0x80 => "Old MINIX",
    0x81 => "MINIX / old Linux",
    0x82 => "Linux swap / Solaris",
    0x83 => "Linux",
    0x84 => "OS/2 hidden C:",
    0x85 => "Linux extended",
    0x86 => "NTFS volume set",
    0x87 => "NTFS volume set",
    0x88 => "Linux plaintext",
    0x8e => "Linux LVM",
    0x93 => "Amoeba",
    0x94 => "Amoeba BBT",
    0x9f => "BSD/OS",
    0xa0 => "IBM Thinkpad hibernation",
    0xa5 => "FreeBSD",
    0xa6 => "OpenBSD",
    0xa7 => "NeXTSTEP",
    0xa8 => "Darwin UFS",
    0xa9 => "NetBSD",
    0xab => "Darwin boot",
    0xb7 => "BSDI fs",
    0xb8 => "BSDI swap",
    0xbb => "Boot Wizard hidden",
    0xbe => "Solaris boot",
    0xbf => "Solaris",
    0xc1 => "DRDOS/sec (FAT-12)",
    0xc4 => "DRDOS/sec (FAT-16 <32M)",
    0xc6 => "DRDOS/sec (FAT-16)",
    0xc7 => "Syrinx",
    0xda => "Non-FS Data",
    0xdb => "CP/M / CTOS",
    0xde => "Dell Utility",
    0xdf => "BootIt",
    0xe1 => "DOS access",
    0xe3 => "DOS R/O",
    0xe4 => "SpeedStor",
    0xeb => "BeOS fs",
    0xee => "EFI GPT",
    0xef => "EFI (FAT-12/16/32)",
    0xf0 => "Linux/PA-RISC boot loader",
    0xf1 => "SpeedStor",
    0xf2 => "DOS secondary",
    0xfb => "VMware File System",
    0xfc => "VMware Swap",
    0xfd => "Linux raid auto",
    0xfe => "LANstep",
    0xff => "BBT",
  }

  UNKNOWN_PARTITION_TYPE = "UNKNOWN".freeze

  def self.partition_type_name(partition_type)
    return PARTITION_TYPE_NAMES[partition_type] if PARTITION_TYPE_NAMES.key?(partition_type)

    UNKNOWN_PARTITION_TYPE
  end

  def partition_type_name
    self.class.partition_type_name(partition_type)
  end

  def alignment_boundary
    # TODO: Base alignment on logical block size of storage
    ::Settings.storage.alignment.boundary.to_i_with_method
  end

  def aligned?
    return nil if start_address.nil?

    # We check all of physical volumes of the VM. This Includes visible and hidden volumes, but excludes logical volumes.
    # The alignment of hidden volumes affects the performance of the logical volumes that are based on them.
    start_address % alignment_boundary == 0
  end
  alias_method :aligned, :aligned?
end
