//
//  Lynkeos
//  SER_Writer.m
//
//  Created by Jean-Etienne LAMIAUD on Wed Dec 20 2023.
//  Copyright (c) 2023. Jean-Etienne LAMIAUD
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//

#include <stdio.h>
#include <math.h>
#include <AppKit/AppKit.h>

#include <LynkeosCore/LynkeosMetadata.h>

#include "SER_Writer.h"

@implementation SER_Writer

+ (void) load
{
   // Nothing to do, this is just to force the runtime to load this class
}

- (id) init
{
   if ( (self = [super init]) != nil )
   {
      _nPlanes = 0;
   }

   return( self );
}

+ (NSString*) writerName { return( @"SER" ); }

+ (NSString*) fileExtension { return( @"ser" ); }

+ (BOOL) canSaveDataWithPlanes:(u_short)nPlanes
                         width:(u_short)w height:(u_short)h
                      metaData:(NSDictionary*)metaData
{
   return( nPlanes == 3 || nPlanes == 1 );
}

- (NSPanel*) configurationPanel
{
   return nil;
}

+ (id <LynkeosFileWriter>) writerForURL:(NSURL*)url
                                 planes:(u_short)nPlanes
                                  width:(u_short)w height:(u_short)h
                               metaData:(NSDictionary*)metaData
{
   SER_Writer *writer = [[[self alloc] init] autorelease];
   writer->_nPlanes = nPlanes;

   return writer;
}

- (u_short) numberOfPlanes {return _nPlanes;}

- (void) saveMovieAtURL:(NSURL *)url
           withDelegate:(id<LynkeosMovieFileWriterDelegate>)delegate
                 opaque:(void *)opaque
             blackLevel:(double)black
             whiteLevel:(double)white
             withPlanes:(u_short)nPlanes
                  width:(u_short)w
                 height:(u_short)h
               metaData:(NSDictionary *)metaData
{
   FILE *file = fopen([[url path] fileSystemRepresentation], "wb");
   const double max = white - black;
   const REAL * const * planes;
   uint16_t *dataLine;
   u_short lineW;
   int32_t count = 0;

   if (file == NULL)
      return;

   SER_Header_t hdr = {
      .FileID = {'0'},
      .LuID = 0,
      .ColorID = (nPlanes == 3 ? SER_RGB : SER_MONO),
      .LittleEndian = 1,
      .ImageWidth = w,
      .ImageHeight = h,
      .PixelDepthPerPlane = 16,
      .FrameCount = 0,               // To be updated later
      .Observer = {0},
      .Instrument = {0},
      .Telescope = {0},
      .DateTime = 0,
      .DateTime_UTC = 0
   };
#if !GNUSTEP
   if (metaData != nil)
   {
      NSString *str;

      NSArray *auth = (NSArray*)[metaData objectForKey:LynkeosMD_Authors()];
      if (auth != nil)
         strncpy(hdr.Observer,
                 [[auth componentsJoinedByString:@" "] UTF8String],
                 SER_STRING_LENGTH);
      str = (NSString*)[metaData objectForKey:LynkeosMD_CameraModel()];
      if (str != nil)
         strncpy(hdr.Instrument, [str UTF8String], SER_STRING_LENGTH);
      str = (NSString*)[metaData objectForKey:LynkeosMD_Telescope()];
      if (str != nil)
         strncpy(hdr.Telescope, [str UTF8String], SER_STRING_LENGTH);
      NSDate *d = (NSDate*)[metaData objectForKey:LynkeosMD_CaptureDate()];
      if (d != nil)
      {
         NSTimeZone *tz = [NSTimeZone systemTimeZone];
         NSTimeInterval o = (NSTimeInterval)[tz secondsFromGMTForDate:d];
         hdr.DateTime_UTC = (int64_t)round([d timeIntervalSinceReferenceDate]/SER_DATE_TIMEBASE)
                            + SER_DATE_ORIGIN;
         hdr.DateTime = (int64_t)round(([d timeIntervalSinceReferenceDate] + o)/SER_DATE_TIMEBASE)
                        + SER_DATE_ORIGIN;
      }
   }
#endif

   if (SER_write_header(file, hdr) < 0)
   {
      fclose(file);
      return;
   }

   dataLine = (uint16_t*)malloc(w*3*sizeof(uint16_t));
   for (;;)
   {
      BOOL canceled;
      [delegate getNextFrameWithData:&planes lineWidth:&lineW opaque:opaque canceled:&canceled];
      if (canceled || planes == NULL)
         break;

      for (u_short y = 0; y < h; y++)
      {
         for (u_short x = 0; x < w; x++)
         {
            for (u_short c = 0; c < nPlanes; c++)
            {
               double v = GET_SAMPLE(planes[c], x, y, lineW)*max;

               if ( v < 0.0 )
                  v = 0.0;
               if ( v > 65535.0 )
                  v = 65535.0;
               dataLine[x*3 + c] = (uint16_t)v;
            }
         }
         if (fwrite(dataLine, sizeof(uint16_t), w*3, file) < 0)
         {
            fclose(file);
            free(dataLine);
            return;
         }
      }

      count++;
   }
   free(dataLine);

   SER_write_frameCount(file, count);
   fclose(file);
}
@end
