public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		
		int lineY = getHeight()*4/5,
				lineWidth = getWidth(),
				lineThickness = getHeight()*1/30,
				dotThickness = lineThickness*3/4,
				markHeight = lineThickness*2,
				markThickness = getWidth()/400 > 0 ? getWidth()/400 : 1;
		
		//set the font size as a function of the width of the episode panel
		g.setFont(new Font("TimesRoman", Font.PLAIN, getWidth()*3/100));
		
		//draw the selected episode box
		if(selected >= 0)
		{
			Episode episode = episodes[selected];
			Ellipse2D selectedCircle = (Ellipse2D)circles.keySet().toArray()[selected];
			
			if(waiting)
				spinner.paintIcon(this, g, getWidth()/2-spinner.getIconWidth()/2, lineY/2-spinner.getIconHeight()/2);
			else
			{
				//highlight the circle
				g.setColor(Color.BLACK);
				((Graphics2D)g).fill(selectedCircle);
				g.setColor(Color.WHITE);
				((Graphics2D)g).draw(selectedCircle);
				
				//get the preloaded episode information
				String[] text = episode.getText().split(" ");
				String[] title = (episode.show+" - "+episode+':').split(" ");
				
				//wrap the text by word
				ArrayList<String> texts, titles;
				Font oldFont = g.getFont();
				do
				{
					texts = new ArrayList<String>();
					titles = new ArrayList<String>();
					for(int j=0; j<text.length; ++j)
					{
						if(texts.size()==0 || 
								g.getFontMetrics().stringWidth(
										texts.get(texts.size()-1) + text[j] + (texts.get(texts.size()-1).length()==0 ? "" : " ")
										) > getWidth()*5/10-5)
							texts.add(text[j]);
						else
							texts.set(texts.size()-1, texts.get(texts.size()-1)+' '+text[j]);
					}
					for(int j=0; j<title.length; ++j)
					{
						if(titles.size()==0 || 
								g.getFontMetrics().stringWidth(
										titles.get(titles.size()-1) + title[j] + (titles.get(titles.size()-1).length()==0 ? "" : " ")
										) > getWidth()*5/10-5)
							titles.add(title[j]);
						else
							titles.set(titles.size()-1, titles.get(titles.size()-1)+' '+title[j]);
					}
					//if the text is bigger than can fit, shrink the font and rewrap
					g.setFont(new Font("TimesRoman", Font.PLAIN, g.getFont().getSize()-1));
				}while((texts.size()+titles.size()) * g.getFontMetrics().getHeight() > getHeight()*9/20);
				
				//draw a box to hold the episode information
				g.setColor(Color.BLACK);
				g.fillRoundRect(getWidth()/20, getHeight()/20, getWidth()*9/10, getHeight()*6/10, 10, 10);
				g.setColor(Color.GREEN);
				g.drawRoundRect(getWidth()/20, getHeight()/20, getWidth()*9/10, getHeight()*6/10, 10, 10);
				
				g.setFont(new Font("TimesRoman", Font.PLAIN, g.getFont().getSize()+1));
				//draw wrapped episode title
				g.setColor(Color.WHITE);
				for(int j=0; j<titles.size(); ++j)
					g.drawString(titles.get(j), getWidth()/10, getHeight()/10+g.getFontMetrics().getAscent()+(g.getFontMetrics().getHeight()*j));
				
				//draw wrapped episode information
				g.setColor(Color.LIGHT_GRAY);
				for(int j=0; j<texts.size(); ++j)
					g.drawString(texts.get(j), getWidth()/10, getHeight()/10+g.getFontMetrics().getAscent()+(g.getFontMetrics().getHeight()*(j+titles.size())));
				
				//reset the old font
				g.setFont(oldFont);
				
				//draw the episode image
				double panelWidth = getWidth()*3/10,
						panelHeight = getHeight()*5/10,
						sourceWidth = episode.getImage().getIconWidth(),
						sourceHeight = episode.getImage().getIconHeight(),
						width, height, xOffset=0, yOffset=0;
				if(sourceHeight/sourceWidth < panelHeight/panelWidth)
				{
					width = panelWidth;
					height = (sourceHeight*panelWidth)/sourceWidth;
					yOffset = ((panelHeight-height)/2) + 1;
				}
				else
				{
					width = (sourceWidth*panelHeight)/sourceHeight;
					height = panelHeight;
					xOffset = ((panelWidth-width)/2) + 1;
				}
				g.drawImage(Main.getScaledInstance(episode.getImage(), (int)width, (int)height), (int)(((getWidth()*6)/10)+xOffset), (int)((getHeight()/10)+yOffset), this);
				g.drawRect((int)(((getWidth()*6)/10)+xOffset), (int)((getHeight()/10)+yOffset), (int)width, (int)height);
			}
		}
		else
			g.drawString("Click an episode to view it's information.", (getWidth()-g.getFontMetrics().stringWidth("Click an episode to view it's information."))/2, lineY/2);
		
		//draw the timeline base line
		g.setColor(Color.DARK_GRAY);
		g.fillRect(0, lineY, lineWidth, lineThickness);

		//draw the day markers on the line
		for(int i=-PAST_DAYS; i<=FUTURE_DAYS; ++i)
		{
			int X = timeToXValue(markers[i+PAST_DAYS], lineWidth);
			if(markers[i+PAST_DAYS] == timelineToday)
			{
				g.setColor(Color.BLACK);
				g.drawString("Today", X-(g.getFontMetrics().stringWidth("Today")/2), lineY+markHeight*3/4+g.getFontMetrics().getAscent());
				g.setColor(Color.BLUE);
				g.fillRect(X-(markThickness/2), lineY, markThickness*2, markHeight*3/4);
			}
			else if(i%7 == 0)
			{
				String weeks = (i>0 ? "+" : "")+i/7+" week"+(Math.abs(i/7)>1 ? "s" : "");
				g.setColor(Color.BLACK);
				int x = X-(g.getFontMetrics().stringWidth(weeks)/2);
				if(x < 0)
					x = 0;
				else if(x+g.getFontMetrics().stringWidth(weeks) > getWidth())
					x = getWidth() - g.getFontMetrics().stringWidth(weeks);
				g.drawString(weeks, x, lineY+markHeight*3/4+g.getFontMetrics().getAscent());
				g.setColor(Color.GREEN);
				g.fillRect(X-(markThickness/2), lineY, markThickness*2, markHeight*3/4);
			}
			else
			{
				g.setColor(Color.RED);
				g.fillRect(X-(markThickness/2), lineY, markThickness, markHeight*3/4);
			}
		}
		
		//draw the episode lines
		int X=-1, Y=lineY;
		g.setColor(Color.BLACK);
		for(int i=0; i<points.length; ++i)
		{
			int newX = timeToXValue(points[i], lineWidth);
			if(X != -1 && X-newX < dotThickness && X-newX > -dotThickness)
				Y = Y-dotThickness;
			else
				Y = lineY;
			X = newX;
			
			//draw line
			g.fillRect(X-(markThickness/2), Y-(markHeight-lineThickness), markThickness, markHeight-(Y-lineY));
		}
		
		//draw the episode circles
		circles = new HashMap<Ellipse2D, Integer>();
		X=-1;
		for(int i=0; i<points.length; ++i)
		{
			//set the color
			try
			{
				g.setColor(new Color(showColors.get(episodes[i].show.showName())));
			}
			catch(NullPointerException npe)
			{
				showColors.put(episodes[i].show.showName(), colorGenerator.nextInt());
				g.setColor(new Color(showColors.get(episodes[i].show.showName())));
			}
			int newX = timeToXValue(points[i], lineWidth);
			if(X != -1 && X-newX < dotThickness && X-newX > -dotThickness)
				Y = Y-dotThickness;
			else
				Y = lineY;
			X = newX;
			
			//create the circle and save it with the associated episode number
			Ellipse2D circle = new Ellipse2D.Double(X-(dotThickness/2), Y-dotThickness-markHeight+lineThickness, dotThickness, dotThickness);
			circles.put(circle, i);
			
			//draw ball
			((Graphics2D)g).fill(circle);
			
			//draw ball outline
			g.setColor(Color.BLACK);
			((Graphics2D)g).draw(circle);
		}
		
		//highlight the selected circle if one is selected
		if(selected >= 0)
		{
			Ellipse2D selectedCircle = null;
			for(Entry<Ellipse2D, Integer> entry : circles.entrySet())
				if(entry.getValue().equals(selected))
				{
					selectedCircle = entry.getKey();
					break;
				}
			
			g.setColor(Color.BLACK);
			((Graphics2D)g).fill(selectedCircle);
			g.setColor(Color.WHITE);
			((Graphics2D)g).draw(selectedCircle);
		}
	}