using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace ChanSort.Api
{
///
/// Reads a reference list from a .csv file with the format
/// [dummy1],ProgramNr,[dummy2],UID,ChannelName[,SignalSource,FavAndFlags]
///
public class CsvFileSerializer : SerializerBase
{
private static readonly List Columns = new List
{
"OldPosition",
"Position",
"Name",
"OriginalNetworkId",
"TransportStreamId",
"ServiceId",
"Favorites",
"Skip",
"Lock",
"Hidden"
};
#region ctor()
public CsvFileSerializer(string fileName) : base(fileName)
{
this.Features.ChannelNameEdit = ChannelNameEditMode.All;
this.Features.CanSkipChannels = true;
this.Features.CanDeleteChannels = true;
this.Features.CanHaveGaps = true;
this.Features.EncryptedFlagEdit = false;
this.DataRoot.SortedFavorites = false;
this.DataRoot.SupportedFavorites = Favorites.A | Favorites.B | Favorites.C | Favorites.D | Favorites.E;
}
#endregion
public override string DisplayName => "ChanSort .csv Reference List Loader";
#region Load()
public override void Load()
{
using (var stream = new StreamReader(this.FileName))
{
var lineNr = 0;
var line = "";
try
{
while ((line = stream.ReadLine()) != null)
this.ReadChannel(line, ++lineNr);
}
catch (Exception ex)
{
throw new FileLoadException($"Error in reference file line #{lineNr}: {line}", ex);
}
}
}
#endregion
#region ReadChannel()
private void ReadChannel(string line, int lineNr)
{
var parts = CsvFile.Parse(line, ',');
if (parts.Count < 5) return;
int programNr;
if (!int.TryParse(parts[1], out programNr)) return;
var uid = parts[3];
if (uid.StartsWith("S")) // remove satellite orbital position from UID ... not all TV models provide this information
uid = "S" + uid.Substring(uid.IndexOf('-'));
var signalSource = GetSignalSource(ref programNr, uid, parts);
if (signalSource == 0)
return;
var channelList = this.GetChannelList(signalSource);
if (channelList == null)
return;
var name = parts[4];
var channel = new ChannelInfo(signalSource, lineNr, programNr, name);
var uidParts = uid.Split('-');
if (uidParts.Length >= 4)
{
int val;
if (int.TryParse(uidParts[1], out val))
channel.OriginalNetworkId = val;
if (int.TryParse(uidParts[2], out val))
channel.TransportStreamId = val;
if (int.TryParse(uidParts[3], out val))
channel.ServiceId = val;
}
if (parts.Count >= 7)
ApplyFlags(channel, parts[6]);
this.DataRoot.AddChannel(channelList, channel);
}
#endregion
#region GetSignalSource()
private static SignalSource GetSignalSource(ref int slot, string uid, IList parts)
{
// new lists store a bitmask which defines the type of channel and list it came from
if (parts.Count >= 6 && parts[5].Length >= 4)
{
SignalSource s = 0;
var code = parts[5];
if (code[0] == 'A') s |= SignalSource.Analog;
else if (code[0] == 'D') s |= SignalSource.Digital;
if (code[1] == 'A') s |= SignalSource.Antenna;
else if (code[1] == 'C') s |= SignalSource.Cable;
else if (code[1] == 'S') s |= SignalSource.Sat;
else if (code[1] == 'I') s |= SignalSource.IP;
if (code[2] == 'T') s |= SignalSource.Tv;
else if (code[2] == 'R') s |= SignalSource.Radio;
s |= (SignalSource) (int.Parse(code.Substring(3)) << 12);
return s;
}
// compatibility for older lists
var isTv = slot < 0x4000;
slot &= 0x3FFFF;
SignalSource signalSource;
switch (uid[0])
{
case 'S':
signalSource = SignalSource.DvbS;
break;
case 'C':
signalSource = SignalSource.DvbCT;
break;
case 'A':
signalSource = SignalSource.AnalogCT;
break;
case 'H':
signalSource = SignalSource.HdPlusD;
break;
default:
return 0;
}
signalSource |= isTv ? SignalSource.Tv : SignalSource.Radio;
return signalSource;
}
#endregion
#region GetChannelList()
private ChannelList GetChannelList(SignalSource signalSource)
{
var channelList = this.DataRoot.GetChannelList(signalSource);
if (channelList == null)
{
channelList = new ChannelList(signalSource, CreateCaption(signalSource));
channelList.VisibleColumnFieldNames = Columns;
this.DataRoot.AddChannelList(channelList);
}
return channelList;
}
#endregion
#region CreateCaption()
private string CreateCaption(SignalSource signalSource)
{
var sb = new StringBuilder();
if ((signalSource & SignalSource.DvbT) == SignalSource.DvbT)
sb.Append("DVB-T");
else if ((signalSource & SignalSource.DvbC) == SignalSource.DvbC)
sb.Append("DVB-C");
else if ((signalSource & SignalSource.DvbS) == SignalSource.DvbS)
sb.Append("DVB-S");
else if ((signalSource & SignalSource.IP) == SignalSource.IP)
sb.Append("IP");
else if ((signalSource & SignalSource.Digital) == SignalSource.Digital)
sb.Append("DVB");
else if ((signalSource & SignalSource.Analog) == SignalSource.Analog)
sb.Append("Analog");
sb.Append(" ");
if ((signalSource & SignalSource.Tv) == SignalSource.Tv)
sb.Append("TV");
else if ((signalSource & SignalSource.Radio) == SignalSource.Radio)
sb.Append("Radio");
else
sb.Append("Data");
return sb.ToString();
}
#endregion
#region ApplyFlags()
private void ApplyFlags(ChannelInfo channel, string flags)
{
channel.Lock = false;
channel.Skip = false;
channel.Hidden = false;
foreach (var c in flags)
{
switch (c)
{
case '1':
channel.Favorites |= Favorites.A;
break;
case '2':
channel.Favorites |= Favorites.B;
break;
case '3':
channel.Favorites |= Favorites.C;
break;
case '4':
channel.Favorites |= Favorites.D;
break;
case '5':
channel.Favorites |= Favorites.E;
break;
case 'L':
channel.Lock = true;
break;
case 'S':
channel.Skip = true;
break;
case 'H':
channel.Hidden = true;
break;
case 'D':
channel.IsDeleted = true;
break;
}
}
}
#endregion
#region EncodeSignalSource()
private static string EncodeSignalSource(SignalSource signalSource)
{
var sb = new StringBuilder();
if ((signalSource & SignalSource.Analog) != 0) sb.Append('A');
else sb.Append('D');
if ((signalSource & SignalSource.Antenna) != 0) sb.Append('A');
else if ((signalSource & SignalSource.Cable) != 0) sb.Append('C');
else if ((signalSource & SignalSource.Sat) != 0) sb.Append('S');
else sb.Append("I");
if ((signalSource & SignalSource.Radio) != 0) sb.Append('R');
else sb.Append('T');
sb.Append((int) signalSource >> 12);
return sb.ToString();
}
#endregion
#region EncodeFavoritesAndFlags()
private static string EncodeFavoritesAndFlags(ChannelInfo channel)
{
var sb = new StringBuilder();
if ((channel.Favorites & Favorites.A) != 0) sb.Append('1');
if ((channel.Favorites & Favorites.B) != 0) sb.Append('2');
if ((channel.Favorites & Favorites.C) != 0) sb.Append('3');
if ((channel.Favorites & Favorites.D) != 0) sb.Append('4');
if ((channel.Favorites & Favorites.E) != 0) sb.Append('5');
if (channel.Lock) sb.Append('L');
if (channel.Skip) sb.Append('S');
if (channel.Hidden) sb.Append('H');
if (channel.IsDeleted) sb.Append('D');
return sb.ToString();
}
#endregion
#region Save()
public override void Save(string tvDataFile)
{
Save(tvDataFile, this.DataRoot);
this.FileName = tvDataFile;
}
public static void Save(string tvDataFile, DataRoot dataRoot)
{
using (var stream = new StreamWriter(tvDataFile))
{
Save(stream, dataRoot);
}
}
public static void Save(StreamWriter stream, DataRoot dataRoot)
{
foreach (var channelList in dataRoot.ChannelLists)
{
if (channelList.IsMixedSourceFavoritesList) // these pseudo-lists would create dupes for all channels
continue;
foreach (var channel in channelList.GetChannelsByNewOrder())
{
if (channel.NewProgramNr == -1)
continue;
var line = string.Format("{0},{1},{2},{3},\"{4}\",{5},{6}",
"", // past: channel.RecordIndex,
channel.NewProgramNr,
"", // past: channel.TransportStreamId,
channel.Uid,
channel.Name,
EncodeSignalSource(channel.SignalSource),
EncodeFavoritesAndFlags(channel));
stream.WriteLine(line);
}
}
}
#endregion
}
}