function cefas2nc(FileName, varargin)
%FileName = 'CEFAS_555352754_1.csv';

OPT.dump              = 0;
OPT.disp              = 0;
OPT.pause             = 0;

OPT.refdatenum        = datenum(0000,0,0); % matlab datenumber convention: A serial date number of 1 corresponds to Jan-1-0000. Gives wring date sin ncbrowse due to different calenders. Must use doubles here.
OPT.refdatenum        = datenum(1970,1,1); % lunix  datenumber convention
OPT.fillvalue         = nan; % NaNs do work in netcdf API

OPT.stationTimeSeries = 0; % last items to adhere to for upcoming convenction, but not yet supported by QuickPlot

%% File loop

OPT.directory_raw     = 'F:\checkouts\OpenEarthRawData\cefas\raw\'; % [];%
OPT.directory_nc      = 'F:\checkouts\OpenEarthRawData\cefas\nc\';                % [];%
OPT.ext               = '';
OPT.mask              = 'id*.txt';
OPT.mask              = 'id*.zip';
OPT.unzip             = 1; % process only zipped files: unzip them, and delete if afterwards
OPT.load              = 1; % load slow *.txt file

OPT = setProperty(OPT, varargin{:});

OPT.directory_nc1  = OPT.directory_nc ;%'F:\checkouts\OpenEarthRawData\rijkswaterstaat\waterbase\nc\'

mkpath(OPT.directory_nc1);

%% Read raw data file
Struct = dir  ([OPT.directory_raw FileName]);
fid    = fopen([OPT.directory_raw FileName], 'r');
iRec = 1;
iLine = 1;
while (~feof(fid))
    FileLine = fgetl(fid);
    if (~isempty(strfind(FileLine, 'Copyright')))
        Copyright{iRec} = FileLine;
    elseif (~isempty(strfind(FileLine, 'Deployment')))
        Deployment{iRec} = FileLine;
    elseif (~isempty(strfind(FileLine, 'Located')))
        Location{iRec} = FileLine;
    elseif (~isempty(strfind(FileLine, 'Data')))
        MeasurementMethod{iRec} = FileLine;
    elseif (~isempty(strfind(FileLine, 'Time')))
        Substances{iRec} = FileLine;
        readString = '%s';
        for i = 1:length(regexp(FileLine, ','))
            readString = [readString '%s'];
        end
        FormatString{iRec} = readString;
        FileLine = fgetl(fid);
        iLine = iLine + 1;
        Units{iRec} = FileLine;
        indStart(iRec) = iLine + 1;
    elseif (length(FileLine) < 2)
        indEnd(iRec) = iLine - 1;
        iRec = iRec + 1;
    end
    iLine = iLine + 1;
end
if (length(indEnd) < length(indStart))
    indEnd(iRec) = iLine;
end
fclose(fid);
nRec = iRec;
fid = fopen([OPT.directory_raw FileName], 'r');
for iRec = 1:nRec
    FileLine = fgetl(fid);
    RawStruct.Location{iRec} = FileLine(1:strfind(FileLine, '©')-2);
    FileLine = fgetl(fid);
    RawStruct.DeploymentID{iRec} = FileLine(15:regexp(FileLine, ':')-1);
    RawStruct.StartDate{iRec} = FileLine(strfind(FileLine, 'from')+5: ...
        strfind(FileLine, 'to')-2);
    RawStruct.EndDate{iRec} = FileLine(strfind(FileLine, 'to')+3:end);
    FileLine = fgetl(fid);
    RawStruct.latitudeString{iRec} = FileLine(12:strfind(FileLine, 'and')-2);
    RawStruct.longitudeString{iRec} = FileLine(strfind(FileLine, 'and')+4: ...
        strfind(FileLine, 'in')-2);
    RawStruct.WaterDepth{iRec} = FileLine(strfind(FileLine, 'in')+4: ...
        strfind(FileLine, 'of')-2);
    RawStruct.MeasurementMethod{iRec} = fgetl(fid);
    RawStruct.Substances{iRec} = textscan(fid, FormatString{iRec}, 1, 'delimiter', ',');
    RawStruct.Units{iRec} = textscan(fid, FormatString{iRec}, 1, 'delimiter', ',');
    RawStruct.Data{iRec} = textscan(fid, FormatString{iRec}, ...
        indEnd(iRec)-indStart(iRec)+1, 'delimiter', ',');
    dummy{iRec} = fgetl(fid);
    dummy{iRec} = fgetl(fid);
end
fclose(fid);

Struct.data.datenum = [];
Struct.data.lat = [];
Struct.data.lon = [];
Struct.data.Value = [];
Struct.data.WaterDepth = [];
Struct.data.units = '';
Struct.data.Parameter = '';
Struct.data.DeploymentID = '';
Struct.data.MeasurementMethod = '';
Struct.data.Station = '';
for iRec = 1:nRec
    Struct.data.datenum = cat(1, Struct.data.datenum, datenum(RawStruct.Data{iRec}{1}));
    Struct.data.datenum = Struct.data.datenum + 1./24;
    clear valArray;
    clear depthArray;
    clear unitArray;
    clear paramArray;
    clear depIDArray;
    clear measArray;
    clear stationArray;
    for iSub = 2:length(RawStruct.Substances{iRec})
        indEmpty = find(strcmp(RawStruct.Data{iRec}{iSub}, ''));
        if (~isempty(indEmpty))
            valArray(indEmpty,iSub-1) = NaN;
        end
        for i = 1:length(indEmpty)
            unitArray(indEmpty(i),iSub-1)  = RawStruct.Units{iRec}{iSub};
            paramArray(indEmpty(i),iSub-1) = RawStruct.Substances{iRec}{iSub};
            stationArray{indEmpty(i),1}    = RawStruct.Location{iRec};
            depthArray{indEmpty(i),1}      = RawStruct.WaterDepth{iRec};
            depIDArray{indEmpty(i),1}      = RawStruct.DeploymentID{iRec};
            measArray{indEmpty(i),1}       = RawStruct.MeasurementMethod{iRec};
        end
        indValid = find(~strcmp(RawStruct.Data{iRec}{iSub}, ''));
        for i = 1:length(indValid)
            valArray(indValid(i),iSub-1)    = ...
                str2num(RawStruct.Data{iRec}{iSub}{indValid(i)});
            unitArray(indValid(i),iSub-1)  = RawStruct.Units{iRec}{iSub};
            paramArray(indValid(i),iSub-1) = RawStruct.Substances{iRec}{iSub};
            stationArray{indValid(i),1}    = RawStruct.Location{iRec};
            depthArray{indValid(i),1}      = RawStruct.WaterDepth{iRec};
            depIDArray{indValid(i),1}      = RawStruct.DeploymentID{iRec};
            measArray{indValid(i),1}       = RawStruct.MeasurementMethod{iRec};
        end
    end
    Struct.data.Value = cat(1, Struct.data.Value, valArray);
    Struct.data.units = cat(1, Struct.data.units, unitArray);
    Struct.data.Parameter = cat(1, Struct.data.Parameter, cellstr(paramArray));
    Struct.data.Station = cat(1, Struct.data.Station, cellstr(stationArray));
    Struct.data.DeploymentID = cat(1, Struct.data.DeploymentID, cellstr(depIDArray));
    Struct.data.WaterDepth = cat(1, Struct.data.WaterDepth, cellstr(depthArray));
    Struct.data.MeasurementMethod = cat(1, Struct.data.MeasurementMethod, cellstr(measArray));
    indDeg = strfind(RawStruct.latitudeString{iRec}, '°');
    indMin = strfind(RawStruct.latitudeString{iRec}, '''');
    indSec = strfind(RawStruct.latitudeString{iRec}, '°');
    stringDeg = RawStruct.latitudeString{iRec}(1:indDeg-1);
    stringMin = RawStruct.latitudeString{iRec}(indDeg+1:indMin-1);
    stringSec = RawStruct.latitudeString{iRec}(indMin+1:end-1);
    latitude = str2num(stringDeg) + str2num([stringMin stringSec])/60.;
    Struct.data.lat = cat(1, Struct.data.lat, latitude + ...
        zeros(size(RawStruct.Data{iRec}{1})));
    indDeg    = strfind(RawStruct.longitudeString{iRec}, '°');
    indMin    = strfind(RawStruct.longitudeString{iRec}, '''');
    indSec    = strfind(RawStruct.longitudeString{iRec}, '°');
    stringDeg = RawStruct.longitudeString{iRec}(1:indDeg-1);
    stringMin = RawStruct.longitudeString{iRec}(indDeg+1:indMin-1);
    stringSec = RawStruct.longitudeString{iRec}(indMin+1:end-1);
    longitude = str2num(stringDeg) + str2num([stringMin stringSec])/60.;
    Struct.data.lon = cat(1, Struct.data.lon, longitude + ...
        zeros(size(RawStruct.Data{iRec}{1})));
end

listStations = {'Gabbard NMMP SmartBuoy', 'North Dogger SmartBuoy for E3205', ...
    'Oyster Ground SmartBuoy', 'Warp (TH1) NMMP SmartBuoy', 'West Gabbard SmartBuoy'};
listLocation = {'Gabbard', 'North Dogger', ...
    'Oyster Ground', 'Warp (TH1)', 'West Gabbard'};
listLocationCode = {'GABBARD', 'NTHDOGGER', 'OYSTER', 'TH1', 'WESTGAB'};
listParameters = {'FTU (@1.0m)', 'SAL (@1.0m)', 'TEMP (@1.0m)'};
listStandardName = {'turbidity', 'salinity', 'temperature'};
listLongName = {'turbidity at 1 m below sea surface', ...
                'salinity at 1 m below sea surface', ...
                'temperature at 1 m below sea surface'};
listUnits = {'FTU', 'pss78', 'degree_Celsius'};

%% Output data in NETCDF files
for iStation = 1:length(listStations)
    disp(['station: ',num2str(iStation)])
    for iParam = 1:length(listParameters)
       disp(['parameter: ',num2str(iParam)])
        idx                  = find(strcmpi(Struct.data.Station, listStations{iStation}));
        D.data.locationcode  = listLocationCode{iStation};
        D.data.location      = listLocation{iStation};
        D.data.longname      = listLongName{iParam};
        D.data.standard_name = listStandardName{iParam};
        dummy                = Struct.data.Value(idx,iParam);
        ind                  = find(isfinite(dummy));
        D.data.(listStandardName{iParam}) = dummy(ind);
        dummy           = Struct.data.datenum(idx);
        D.data.datenum  = dummy(ind);
        D.data.lon      = mean(Struct.data.lon(idx));
        D.data.lat      = mean(Struct.data.lat(idx));
        D.data.units    = listUnits{iParam};
        D.meta1.what    = '1 m below surface';
        D.meta1.observationtype = Struct.data.MeasurementMethod{idx(1)};
        D.filename     = Struct.name;
        D.date         = Struct.date;
        if (~isempty(ind))
            OPT.name          = listStandardName{iParam};
            OPT.long_name     = listLongName{iParam};
            OPT.units         = listUnits{iParam};
            OPT.standard_name = listStandardName{iParam};
            OPT.fillvalue     = NaN;
            OPT.filename      = ['id' sprintf('%d', iParam) '-' D.data.locationcode ...
                '-196101010000-' datestr(D.date, 'yyyymmddHHMM') '.nc']; % id1-AMRGBVN-196101010000-200801010000.txt
            %% 0 Create file
            outputfile    = fullfile(OPT.directory_nc1,filesep,OPT.name,filesep,OPT.filename);
            if ~exist(fileparts(outputfile))
                error(['please create yourselves: ',fileparts(outputfile)])
            end
            nc_create_empty (outputfile)
            %% 1 Add global meta-info to file
            % Add overall meta info:
            % http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#description-of-file-contents
            nc_attput(outputfile, nc_global, 'title'           , '');
            nc_attput(outputfile, nc_global, 'institution'     , 'CEFAS');
            nc_attput(outputfile, nc_global, 'source'          , 'Smartbuoy');
            nc_attput(outputfile, nc_global, 'history'         , ['Original filename: ',filename(D.filename),...
                ', filedate:',D.date,...
                ', tranformation to netCDF: $HeadURL$ $Revision$ $Date$ $Author$']);
            nc_attput(outputfile, nc_global, 'references'      , '<http://www.cefas.co.uk/products-and-services/environmental-monitoring-equipment/smartbuoy.aspx>,<http://openearth.deltares.nl>');
            nc_attput(outputfile, nc_global, 'email'           , '<marketing@cefas.co.uk>');
            nc_attput(outputfile, nc_global, 'comment'         , '');
            nc_attput(outputfile, nc_global, 'Conventions'     , 'CF-1.4');
            nc_attput(outputfile, nc_global, 'CF:featureType'  , 'stationTimeSeries');  % https://cf-pcmdi.llnl.gov/trac/wiki/PointObservationConventions
            nc_attput(outputfile, nc_global, 'terms_for_use'   , 'These data can be used freely for research purposes provided that the following source is acknowledged: Rijkswaterstaat.');
            nc_attput(outputfile, nc_global, 'disclaimer'      , 'This data is made available 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.');
            nc_attput(outputfile, nc_global, 'stationname'     , D.data.location);
            nc_attput(outputfile, nc_global, 'location'        , D.data.location);
            nc_attput(outputfile, nc_global, 'cefas_code'      , D.data.locationcode);
            nc_attput(outputfile, nc_global, 'locationcode'    , D.data.locationcode);
            nc_attput(outputfile, nc_global, 'observation_type', D.meta1.observationtype);
            nc_attput(outputfile, nc_global, 'reference_level' , D.meta1.what);
            %% 2 Create dimensions
            nc_add_dimension(outputfile, 'time'        , length(D.data.datenum))
            nc_add_dimension(outputfile, 'locations'   , 1);
            nc_add_dimension(outputfile, 'name_strlen1', length(D.data.locationcode)); % for multiple stations get max length
            nc_add_dimension(outputfile, 'name_strlen2', length(D.data.location    )); % for multiple stations get max length
            %% 3 Create variables
            clear nc
            ifld = 0;

            % Station number: allows for exactly same variables when multiple
            % timeseries in one netCDF file (future extension)

            ifld = ifld + 1;
            nc(ifld).Name         = 'station_id';
            nc(ifld).Nctype       = 'char';
            nc(ifld).Dimension    = {'locations','name_strlen1'};
            nc(ifld).Attribute(1) = struct('Name', 'long_name'      ,'Value', 'station identification code');
            nc(ifld).Attribute(2) = struct('Name', 'standard_name'  ,'Value', 'station_id'); % standard name

            % Station long name

            ifld = ifld + 1;
            nc(ifld).Name         = 'station_name';
            nc(ifld).Nctype       = 'char';
            nc(ifld).Dimension    = {'locations','name_strlen2'};
            nc(ifld).Attribute(1) = struct('Name', 'long_name'      ,'Value', 'station name');

            % Define dimensions in this order:
            % [time,z,y,x]
            %
            % For standard names see:
            % http://cf-pcmdi.llnl.gov/documents/cf-standard-names/standard-name-table/current/standard-name-table
            % Longitude:
            % http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#longitude-coordinate

            ifld = ifld + 1;
            nc(ifld).Name         = 'lon';
            nc(ifld).Nctype       = 'float'; % no double needed
            nc(ifld).Dimension    = {'locations'};
            nc(ifld).Attribute(1) = struct('Name', 'long_name'      ,'Value', 'station longitude');
            nc(ifld).Attribute(2) = struct('Name', 'units'          ,'Value', 'degrees_east');
            nc(ifld).Attribute(3) = struct('Name', 'standard_name'  ,'Value', 'longitude');

            % Latitude:
            % http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#latitude-coordinate

            ifld = ifld + 1;
            nc(ifld).Name         = 'lat';
            nc(ifld).Nctype       = 'float'; % no double needed
            nc(ifld).Dimension    = {'locations'};
            nc(ifld).Attribute(1) = struct('Name', 'long_name'      ,'Value', 'station latitude');
            nc(ifld).Attribute(2) = struct('Name', 'units'          ,'Value', 'degrees_north');
            nc(ifld).Attribute(3) = struct('Name', 'standard_name'  ,'Value', 'latitude');

            % Time:
            % http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#time-coordinate
            % time is a dimension, so there are two options:
            % * the variable name needs the same as the dimension:
            %   http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#id2984551
            % * there needs to be an indirect mapping through the coordinates attribute:
            %   http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#id2984605

            OPT.timezone = timezone_code2iso('MET');

            ifld = ifld + 1;
            nc(ifld).Name         = 'time';
            nc(ifld).Nctype       = 'double'; % float not sufficient as datenums are big: doubble
            if OPT.stationTimeSeries
            nc(ifld).Dimension    = {'locations','time'}; % QuickPlot error: plots dimensions instead of datestr
            else
            nc(ifld).Dimension    = {'time'}; % {'locations','time'} % does not work in ncBrowse, nor in Quickplot (is indirect time mapping)
            end
            nc(ifld).Attribute(1) = struct('Name', 'long_name'      ,'Value', 'time');
            nc(ifld).Attribute(2) = struct('Name', 'units'          ,'Value', ['days since ',datestr(OPT.refdatenum,'yyyy-mm-dd'),' 00:00:00 ',OPT.timezone]);
            nc(ifld).Attribute(3) = struct('Name', 'standard_name'  ,'Value', 'time');
            nc(ifld).Attribute(4) = struct('Name', '_FillValue'     ,'Value', OPT.fillvalue);

            %nc(ifld).Attribute(5) = struct('Name', 'bounds'         ,'Value', '');

            % Parameters with standard names:
            % * http://cf-pcmdi.llnl.gov/documents/cf-standard-names/standard-name-table/current/standard-name-table/

            ifld = ifld + 1;
            nc(ifld).Name         = OPT.name;
            nc(ifld).Nctype       = 'float'; % no double needed
            nc(ifld).Dimension    = {'locations','time'};
            nc(ifld).Attribute(1) = struct('Name', 'long_name'      ,'Value', OPT.long_name);
            nc(ifld).Attribute(2) = struct('Name', 'units'          ,'Value', OPT.units);
            nc(ifld).Attribute(3) = struct('Name', 'standard_name'  ,'Value', OPT.standard_name);
            nc(ifld).Attribute(4) = struct('Name', '_FillValue'     ,'Value', OPT.fillvalue);
            nc(ifld).Attribute(5) = struct('Name', 'cell_methods'   ,'Value', 'time: point area: point');
            if OPT.stationTimeSeries
            nc(ifld).Attribute(6) = struct('Name', 'coordinates'    ,'Value', 'lat lon');  % QuickPlot error
            end

            %% 4 Create variables with attibutes
            % When variable definitons are created before actually writing the
            % data in the next cell, netCDF can nicely fit all data into the
            % file without the need to relocate any info.

            for ifld=1:length(nc)
                if OPT.disp;disp([num2str(ifld),' ',nc(ifld).Name]);end
                nc_addvar(outputfile, nc(ifld));
            end
            nc_varput(outputfile, 'station_id'  , D.data.locationcode);
            nc_varput(outputfile, 'station_name', D.data.location);
            nc_varput(outputfile, 'lon'         , unique(D.data.lon));
            nc_varput(outputfile, 'lat'         , unique(D.data.lat));
            nc_varput(outputfile, 'time'        , D.data.datenum' - OPT.refdatenum);
            nc_varput(outputfile, OPT.name      , D.data.(OPT.name));
        end
    end
end

%% Build overview files

nc_cf_stationtimeseries2meta('directory_nc', [OPT.directory_nc 'turbidity' filesep], ...
    'standard_names', {'turbidity'});
nc_cf_stationtimeseries2kmloverview([OPT.directory_nc 'turbidity.xls'], ...
    'fileName', [OPT.directory_nc 'turbidity.kml'], ...
    'standard_name', 'turbidity');

nc_cf_stationtimeseries2meta('directory_nc', [OPT.directory_nc 'salinity' filesep], ...
    'standard_names', {'salinity'});
nc_cf_stationtimeseries2kmloverview([OPT.directory_nc 'salinity.xls'], ...
    'fileName', [OPT.directory_nc 'salinity.kml'], ...
    'standard_name', 'salinity');

nc_cf_stationtimeseries2meta('directory_nc', [OPT.directory_nc 'temperature' filesep], ...
    'standard_names', {'temperature'});
nc_cf_stationtimeseries2kmloverview([OPT.directory_nc 'temperature.xls'], ...
    'fileName', [OPT.directory_nc 'temperature.kml'], ...
    'standard_name', 'temperature');

return;

end
%% EOF