(*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * http://www.opensource.org/licenses/cddl1.php.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * http://www.opensource.org/licenses/cddl1.php.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Andreas Schneider
 *)
unit URadarMap;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, UConfig, UNetState, UEnhancedMemoryStream, UEnums;
  
type

  TRadarColorArray = array of Word;

  { TRadarMap }

  TRadarMap = class(TObject)
    constructor Create(AMap, AStatics, AStaIdx: TStream; AWidth, AHeight: Word;
      ARadarCol: string);
    destructor Destroy; override;
  protected
    FWidth: Word;
    FHeight: Word;
    FRadarColors: TRadarColorArray;
    FRadarMap: TRadarColorArray;
    FPackets: TList;
    FPacketSize: Cardinal;
    procedure OnRadarHandlingPacket(ABuffer: TEnhancedMemoryStream;
      ANetState: TNetState);
  public
    procedure Update(AX, AY, ATileID: Word);
    procedure BeginUpdate;
    procedure EndUpdate;
  end;

implementation

uses
  UPacket, UPackets, UPacketHandlers, UCEDServer, crc, UStatics;

type
  TMulIndex = packed record
    Position: Cardinal;
    Size: Cardinal;
    Userdata: Cardinal;
  end;
  TMapCell = packed record
    TileID: Word;
    Altitude: ShortInt;
  end;
  TOldStaticItem = packed record
    Unknown: Cardinal;
    TileID: Word;
    X, Y: Byte;
    Z: ShortInt;
    Hue: Word;
  end;
  TStaticItem = packed record
    TileID: Word;
    X, Y: Byte;
    Z: ShortInt;
    Hue: Word;
  end;
  
  { TRadarChecksumPacket }

  TRadarChecksumPacket = class(TPacket)
    constructor Create(ARadarMap: TRadarColorArray);
  end;
  
  { TRadarMapPacket }

  TRadarMapPacket = class(TPacket)
    constructor Create(ARadarMap: TRadarColorArray);
  end;
  
  { TUpdateRadarPacket }

  TUpdateRadarPacket = class(TPacket)
    constructor Create(AX, AY, AColor: Word);
  end;

{ TRadarChecksumPacket }

constructor TRadarChecksumPacket.Create(ARadarMap: TRadarColorArray);
var
  checksum: Cardinal;
begin
  inherited Create($0D, 0);
  FStream.WriteByte($01);
  checksum := crc32(0, nil, 0);
  checksum := crc32(checksum, @ARadarMap[0], Length(ARadarMap) * SizeOf(Word));
  FStream.WriteCardinal(checksum);
end;

{ TRadarMapPacket }

constructor TRadarMapPacket.Create(ARadarMap: TRadarColorArray);
begin
  inherited Create($0D, 0);
  FStream.WriteByte($02);
  FStream.Write(ARadarMap[0], Length(ARadarMap) * SizeOf(Word));
end;

{ TUpdateRadarPacket }

constructor TUpdateRadarPacket.Create(AX, AY, AColor: Word);
begin
  inherited Create($0D, 0);
  FStream.WriteByte($03);
  FStream.WriteWord(AX);
  FStream.WriteWord(AY);
  FStream.WriteWord(AColor);
end;

{ TRadarMap }

constructor TRadarMap.Create(AMap, AStatics, AStaIdx: TStream; AWidth,
  AHeight: Word; ARadarCol: string);
var
  radarcol: TFileStream;
  count, i, item, highestZ: Integer;
  staticsItems: array of TStaticItem;
  oldStaticsItems: array of TOldStaticItem;
  mapCell: TMapCell;
  index: TMulIndex;
begin
  radarcol := TFileStream.Create(ARadarCol, fmOpenRead);
  SetLength(FRadarColors, radarcol.Size div SizeOf(Word));
  radarcol.Read(FRadarColors[0], radarcol.Size);
  radarcol.Free;
  
  FWidth := AWidth;
  FHeight := AHeight;
  
  count := AWidth * AHeight;
  SetLength(FRadarMap, count);
  
  AMap.Position := 4;
  AStaIdx.Position := 0;

  for i := 0 to count - 1 do
  begin
    AMap.Read(mapCell, SizeOf(TMapCell));
    AMap.Seek(193, soFromCurrent);
    FRadarMap[i] := FRadarColors[mapCell.TileID];
    AStaIdx.Read(index, SizeOf(TMulIndex));
    if (index.Position < $FFFFFFFF) and (index.Size > 0)
       and (index.Position + index.Size < AStatics.Size) then
    begin
      AStatics.Position := index.Position;
      if not UseStaticsOldFormat then
      begin
        SetLength(staticsItems, index.Size div 7);
        AStatics.Read(staticsItems[0], index.Size);
        highestZ := mapCell.Altitude;
        for item := Low(staticsItems) to High(staticsItems) do
        begin
          if (staticsItems[item].X = 0) and (staticsItems[item].Y = 0) and
            (staticsItems[item].Z >= highestZ) then
          begin
            highestZ := staticsItems[item].Z;
            FRadarMap[i] := FRadarColors[staticsItems[item].TileID + $4000];
          end;
        end;
      end else begin
        SetLength(oldStaticsItems, index.Size div 11);
        AStatics.Read(oldStaticsItems[0], index.Size);
        highestZ := mapCell.Altitude;
        for item := Low(oldStaticsItems) to High(oldStaticsItems) do
        begin
          if (oldStaticsItems[item].X = 0) and (oldStaticsItems[item].Y = 0) and
            (oldStaticsItems[item].Z >= highestZ) then
          begin
            highestZ := oldStaticsItems[item].Z;
            FRadarMap[i] := FRadarColors[oldStaticsItems[item].TileID + $4000];
          end;
        end;
      end;

    end;
  end;
  
  FPackets := nil;
  
  RegisterPacketHandler($0D, TPacketHandler.Create(2, @OnRadarHandlingPacket));
  
  inherited Create;
end;

destructor TRadarMap.Destroy;
begin
  RegisterPacketHandler($0D, nil);
  inherited Destroy;
end;

procedure TRadarMap.OnRadarHandlingPacket(ABuffer: TEnhancedMemoryStream;
  ANetState: TNetState);
var
  subID: Byte;
begin
  if not ValidateAccess(ANetState, alView) then Exit;
  
  subID := ABuffer.ReadByte;
  case subID of
    $01: //request checksum
      begin
        CEDServerInstance.SendPacket(ANetState, TRadarChecksumPacket.Create(
          FRadarMap));
      end;
    $02: //request radarmap
      begin
        CEDServerInstance.SendPacket(ANetState, TCompressedPacket.Create(
          TRadarMapPacket.Create(FRadarMap)));
      end;
  end;
end;

procedure TRadarMap.Update(AX, AY, ATileID: Word);
var
  color: Word;
  block: Cardinal;
  packet: TPacket;
begin
  block := AX * FHeight + AY;
  color := FRadarColors[ATileID];
  if FRadarMap[block] <> color then
  begin
    FRadarMap[block] := color;
    packet := TUpdateRadarPacket.Create(AX, AY, color);
    if FPackets <> nil then
    begin
      FPackets.Add(packet);
      Inc(FPacketSize, packet.Stream.Size);
    end else
      CEDServerInstance.SendPacket(nil, packet);
  end;
end;

procedure TRadarMap.BeginUpdate;
begin
  if FPackets <> nil then Exit;
  FPackets := TList.Create;
  FPacketSize := 0;
end;

procedure TRadarMap.EndUpdate;
var
  completePacket: TPacket;
  i: Integer;
begin
  if FPackets = nil then Exit;
  completePacket := TCompressedPacket.Create(TRadarMapPacket.Create(FRadarMap));
  if completePacket.Stream.Size <= (FPacketSize div 4) * 5 then
  begin
    CEDServerInstance.SendPacket(nil, completePacket);
    for i := 0 to FPackets.Count - 1 do
      TPacket(FPackets.Items[i]).Free;
  end else
  begin
    for i := 0 to FPackets.Count - 1 do
      CEDServerInstance.SendPacket(nil, TPacket(FPackets.Items[i]));
    completePacket.Free;
  end;
  FreeAndNil(FPackets);
end;

end.

